nibula 2.4.3 → 2.4.5

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/docs/Deploy.md DELETED
@@ -1,279 +0,0 @@
1
- # Deploy
2
-
3
- Nibula builds your finished site into a folder called `out`. Publishing means
4
- copying that folder onto a web server so people can reach it.
5
-
6
- How you do that depends on two things: **where** you are publishing, and **which
7
- backend** you picked when you created the project (see `docs/Backend.md`).
8
-
9
- ## Start here
10
-
11
- If you don't know which case you are in, answer this first:
12
-
13
- **Did you choose the PHP backend or the Node backend?**
14
-
15
- - **PHP** → almost anywhere works, including cheap hosting plans. Jump to
16
- *Shared hosting* or *Windows hosting*.
17
- - **Node** → you need a server you control. Jump to *Your own server*.
18
- - **No `/api` endpoints at all?** Your site is fully static. Anything works,
19
- including free hosting like Netlify or GitHub Pages.
20
-
21
- ## Why the backend decides everything
22
-
23
- The two backends run in completely different ways, and this is the one thing
24
- worth understanding before you pick a host.
25
-
26
- **PHP** runs **once per visit**. A request arrives, the web server starts PHP,
27
- PHP answers, PHP exits. Nothing stays running, so there is nothing to look after.
28
-
29
- **Node is itself a server**. It is a program that must stay running all the time,
30
- listening in the background, while the web server forwards `/api` requests to it.
31
- Somebody has to start that program and restart it if the machine reboots or the
32
- program crashes.
33
-
34
- That is why **cheap shared hosting cannot run the Node backend**. On plans like
35
- Aruba or cPanel you upload files into a folder and that's all you can do — there
36
- is no way to start a program and keep it alive. If that's your host, choose PHP.
37
- It isn't a limitation of Nibula.
38
-
39
- > **Shared hosting** means a plan where you share a server with other customers
40
- > and only get a folder to upload into. **A VPS** is a whole machine that is
41
- > yours, where you can install and run whatever you want.
42
-
43
- ## Where you can publish
44
-
45
- | Target | Frontend | PHP backend | Node backend |
46
- |---|---|---|---|
47
- | Shared hosting (Apache — Aruba, cPanel) | ✅ | ✅ | ❌ no program can stay running |
48
- | Windows hosting (IIS) | ✅ | ✅ | ⚠️ only with the ARR module |
49
- | Your own VPS (Nginx on Ubuntu) | ✅ | ✅ | ✅ |
50
- | Static hosting (Netlify, Vercel, GitHub Pages) | ✅ | ❌ | ❌ |
51
-
52
- Static hosting serves files and nothing else. Perfect if your site has no `/api`
53
- endpoints, unusable if it does.
54
-
55
- ## What you need
56
-
57
- **PHP backend:** a host with PHP support, and Composer to install the backend's
58
- dependencies.
59
-
60
- **Node backend:** Node.js 18 or newer, and command-line access to the server so
61
- you can start the backend program.
62
-
63
- ## Editing the server config
64
-
65
- Every server needs a configuration file telling it how to behave — which page to
66
- show on a 404, which files to keep private, where to send `/api` requests. Nibula
67
- writes these for you.
68
-
69
- `.htaccess` (Apache) and `web.config` (IIS) are already inside `out`, ready to
70
- upload. To change a rule, edit the original in `src/frontend/hosting/` — anything
71
- you change directly inside `out` is erased the next time you build.
72
-
73
- `nginx.conf` is different. It sits in your project root and is **not** copied into
74
- `out`, because Nginx does not read config files from the site folder. You install
75
- it on the server yourself, as described below.
76
-
77
- ---
78
-
79
- ## Shared hosting (Apache)
80
-
81
- Upload **the contents of `out`** — not the folder itself — into your web root.
82
- Depending on the host it is called `htdocs`, `public_html` or `www`.
83
-
84
- That's it. `.htaccess` is already in there and Apache reads it automatically:
85
- folder listings are off, errors show your `/404.html`, sensitive files are
86
- blocked, the backend source is sealed off, and `/api/*` is routed.
87
-
88
- **How `/api` gets routed:** if the server has a module called `mod_proxy`, `/api`
89
- is forwarded to Node. If it doesn't — the usual case on shared hosting — `/api`
90
- goes to PHP instead. To force PHP on a server that does have `mod_proxy`, comment
91
- out the `mod_proxy` block in `.htaccess`.
92
-
93
- ## Windows hosting (IIS)
94
-
95
- Same as above: upload the contents of `out`. `web.config` is already there and
96
- read automatically.
97
-
98
- **How `/api` gets routed:** to PHP, by default. To use Node instead, uncomment
99
- the `ApiToNode` rule in `web.config` and delete `ApiToPhp`. This only works if
100
- the server has the ARR and URL Rewrite modules with proxying turned on — which
101
- means a server you control, not shared hosting.
102
-
103
- > ⚠️ **If you chose Node and your host has no ARR** (Aruba and most Windows
104
- > shared hosting), do **not** upload the `backend` folder. Without it the static
105
- > site works fine and `/api/*` simply shows your `404.html`. Upload it and every
106
- > `/api` request returns an empty 404 instead, because IIS sends the request to
107
- > the PHP front controller — a file a Node project doesn't have.
108
-
109
- ---
110
-
111
- ## Your own server (Nginx on a VPS)
112
-
113
- Nginx doesn't read config files from the site folder: the whole site is described
114
- in one file kept elsewhere on the machine. Nibula ships that file, ready to use,
115
- as `nginx.conf` in your project root.
116
-
117
- It **handles both backends with no edits**: `/api` goes to Node, and if Node
118
- isn't running the request falls through to PHP automatically.
119
-
120
- Upload the contents of `out` to `/var/www/SITE_FOLDER`, then work through the
121
- steps below.
122
-
123
- ### 1. Fill in the placeholders
124
-
125
- Open `nginx.conf` on your computer and replace these four words with your own
126
- values:
127
-
128
- | Placeholder | What to put there |
129
- |---|---|
130
- | `YOUR_DOMAIN` | your domain — appears twice, once for port 80 and once for 443 |
131
- | `SITE_FOLDER` | the folder name you used under `/var/www/` |
132
- | `YOUR_CERTIFICATE` | the folder name under `/etc/letsencrypt/live/` |
133
- | `SITE_NAME` | the file name you'll use in the commands below — pick anything |
134
-
135
- ### 2. Install what's missing
136
-
137
- Skip anything already installed.
138
-
139
- ```bash
140
- sudo apt update
141
- sudo apt install -y nginx certbot python3-certbot-nginx
142
- ```
143
-
144
- With the PHP backend, also:
145
-
146
- ```bash
147
- sudo apt install -y php-fpm
148
- ```
149
-
150
- ### 3. Get an HTTPS certificate
151
-
152
- This is what makes your site load over `https://` instead of `http://`.
153
- Certificates are free and last 90 days; the second command renews them
154
- automatically.
155
-
156
- ```bash
157
- sudo certbot certonly --nginx --cert-name SITE_NAME -d YOUR_DOMAIN
158
- sudo systemctl enable --now certbot.timer
159
- ```
160
-
161
- ### 4. Install the site file
162
-
163
- ```bash
164
- # Go to the folder where Nginx keeps site files
165
- cd /etc/nginx/sites-available
166
-
167
- # Create the file and paste in the contents of your edited nginx.conf
168
- sudo nano SITE_NAME
169
- # Paste, then: CTRL + O, ENTER, CTRL + X
170
-
171
- # Switch the site on by copying it into sites-enabled
172
- sudo cp SITE_NAME ../sites-enabled/
173
-
174
- # Check the config for mistakes, then apply it
175
- sudo nginx -t && sudo systemctl reload nginx
176
- ```
177
-
178
- If `nginx -t` reports an error, fix it before reloading — Nginx keeps running on
179
- the old config until the reload succeeds, so your site stays up.
180
-
181
- ### If something goes wrong
182
-
183
- | What you see | What it usually means |
184
- |---|---|
185
- | `cannot load certificate ... Permission denied` | you ran `nginx -t` without `sudo` |
186
- | `cannot load certificate ... No such file` | wrong certificate name |
187
- | `no alternative certificate subject name` | the certificate doesn't cover this domain |
188
- | `unknown directive "http2"` | Nginx older than 1.25 — check with `nginx -v` |
189
- | NXDOMAIN or a timeout during certbot | your domain's DNS isn't pointing at this server yet |
190
- | A blank 500 instead of your 404 page | `404.html` is missing from the upload |
191
- | 502 on `/api`, Node backend | the Node program isn't running |
192
- | 502 on `/api`, PHP backend | wrong php-fpm socket path in the config |
193
-
194
- ---
195
-
196
- ## Starting the Node backend on the server
197
-
198
- Only for the Node backend. After uploading `out`:
199
-
200
- **1. Install its dependencies**
201
-
202
- ```bash
203
- cd /var/www/SITE_FOLDER/backend
204
- npm install
205
- ```
206
-
207
- **2. Create its config file**
208
-
209
- Make sure `config.js` exists in `out/backend/`. If it doesn't, copy
210
- `example.config.js` to `config.js` and fill in your values.
211
-
212
- **3. Try it out first**
213
-
214
- `screen` lets you start a program, walk away, and come back to it later. Use it
215
- to check the backend actually works before setting it up properly.
216
-
217
- ```bash
218
- screen -S node-backend
219
- # Inside the new screen:
220
- sudo node /var/www/SITE_FOLDER/backend/_core/index.js
221
- curl http://127.0.0.1:3000/api/example-public
222
- ```
223
-
224
- If `curl` prints a JSON response, the backend is alive. Press `CTRL + A` then `D`
225
- to leave the screen without stopping the program; `screen -r node-backend` brings
226
- you back.
227
-
228
- **4. Then set it up for real**
229
-
230
- `screen` is for testing — the program dies if the server reboots. For a live site
231
- use **systemd**, the part of Linux that manages background programs. It starts
232
- your backend at boot and restarts it if it crashes.
233
-
234
- Create `/etc/systemd/system/backend-node.service`:
235
-
236
- ```ini
237
- [Unit]
238
- Description=Nibula Node backend
239
- After=network.target
240
-
241
- [Service]
242
- Type=simple
243
- User=www-data
244
- WorkingDirectory=/var/www/SITE_FOLDER/backend
245
- ExecStart=/usr/bin/node /var/www/SITE_FOLDER/backend/_core/index.js
246
- Restart=always
247
- RestartSec=5
248
-
249
- [Install]
250
- WantedBy=multi-user.target
251
- ```
252
-
253
- Adjust `WorkingDirectory`, and check the path to `node` with `which node`. Then:
254
-
255
- ```bash
256
- sudo systemctl daemon-reload
257
- sudo systemctl enable --now backend-node
258
- sudo systemctl status backend-node
259
- ```
260
-
261
- The last command should say `active (running)`.
262
-
263
- (If you already use `pm2`, that works too:
264
- `pm2 start _core/index.js --name backend-node`.)
265
-
266
- ### Settings the Node backend reads at startup
267
-
268
- These are set in the systemd file, on their own lines, as
269
- `Environment=KEY=value`. All three have sensible defaults — you usually don't
270
- need to touch them.
271
-
272
- | Setting | Default | What it does |
273
- |---|---|---|
274
- | `PORT` | `3000` | the port Node listens on — must match the one in `nginx.conf` |
275
- | `HOST` | `127.0.0.1` | keep this as-is: it means "local only", so Node is reachable through the web server and not directly from the internet |
276
- | `DOCUMENT_ROOT` | detected automatically | where `404.html` lives — the folder above `backend/` |
277
-
278
- > Error detail is **not** set here. That's the `APP_ENV` key inside `config.js` /
279
- > `config.php` — see `docs/Backend.md`.
@@ -1,198 +0,0 @@
1
- # Head & SEO
2
-
3
- The `<head>` of an HTML page holds the information *about* the page rather than
4
- its content: the title shown in the browser tab, the description search engines
5
- display, the image that appears when someone shares the link. Nibula writes all
6
- of it for you — you just fill in the values.
7
-
8
- There are two files to fill in:
9
-
10
- - **`src/frontend/data/site.json`** — settings for the whole site
11
- - **`src/frontend/data/pages.json`** — settings for one page at a time
12
-
13
- A page uses its own value when it has one, and falls back to the site-wide value
14
- when it doesn't. So filling in `site.json` well means most pages need nothing.
15
-
16
- ## Site-wide settings
17
-
18
- Everything in `site.json` is available anywhere in your templates as
19
- `{{ site.something }}`.
20
-
21
- ```json
22
- {
23
- "site_name": "Site name",
24
- "title": "Site title",
25
- "description": "Site description",
26
- "keywords": "keyword1, keyword2, keyword3",
27
- "domain": "yoursite.com",
28
- "url": "https://yoursite.com",
29
- "lang": "en",
30
- "author": "Name and surname",
31
- "theme": "dark",
32
- "theme_color": "#2a2150",
33
- "logo": "/assets/brand/logo.svg",
34
- "legal": {
35
- "privacy": "",
36
- "cookie": "",
37
- "terms": "",
38
- "copyright": {
39
- "year": "2026",
40
- "text": "Copyright text"
41
- }
42
- }
43
- }
44
- ```
45
-
46
- | Field | What it's for |
47
- |---|---|
48
- | `site_name` | Your brand or project name |
49
- | `title` | Default page title — used by any page that doesn't set its own |
50
- | `description` | Default description — same idea |
51
- | `keywords` | Default keywords — same idea |
52
- | `domain` / `url` | Used to build full page addresses for search engines and social previews |
53
- | `lang` | The language of your site (`en`, `it`, ...) |
54
- | `author` | Your name |
55
- | `theme` | Colour scheme of the site: `light` or `dark` |
56
- | `theme_color` | Colour of the browser bar on phones — should match your theme |
57
- | `logo` | Path to your logo, also used as the image in social previews |
58
- | `legal.privacy` / `cookie` / `terms` | Addresses of your legal pages — the footer links to them |
59
- | `legal.copyright.year` / `text` | The copyright line in the footer |
60
-
61
- ## Per-page settings
62
-
63
- `pages.json` holds one entry per page, and the assistant creates it for you when
64
- you add a page. Each entry is keyed by the page's `title` from its front matter —
65
- the camelCase one.
66
-
67
- ```json
68
- {
69
- "examplePage": {
70
- "seo": {
71
- "title": "Example Page",
72
- "description": "Description",
73
- "keywords": "",
74
- "noindex": false,
75
- "canonical": ""
76
- },
77
- "cdn": {
78
- "css": ["https://example.com/lib.min.css"],
79
- "js": ["https://example.com/lib.min.js"]
80
- }
81
- }
82
- }
83
- ```
84
-
85
- | Field | What it's for | If you leave it empty |
86
- |---|---|---|
87
- | `seo.title` | The page's title, shown in the browser tab and in search results | Uses the site-wide `title` |
88
- | `seo.description` | The short summary search engines show under the title | Uses the site-wide `description` |
89
- | `seo.keywords` | Keywords for this page | Uses the site-wide `keywords` |
90
- | `seo.noindex` | Set to `true` to ask search engines **not** to list this page | The page can be found normally |
91
- | `seo.canonical` | The one official address of this page, if the same content is reachable at more than one URL | Worked out automatically |
92
- | `cdn.css` / `cdn.js` | Extra stylesheets or scripts loaded from the internet, on this page only | Nothing extra is loaded |
93
-
94
- > ⚠️ The assistant fills new entries with placeholder text like `"Description"`.
95
- > Replace it — a page left that way tells search engines and AI crawlers that its
96
- > description is literally "Description".
97
-
98
- ### How the fallback works
99
-
100
- Site-wide values are defaults, not duplicates. You don't need to repeat them per
101
- page. This is what the template actually does:
102
-
103
- ```njk
104
- {{ pageData.seo.title or title or site.title }}
105
- {{ pageData.seo.description or site.description }}
106
- {{ pageData.seo.keywords or site.keywords }}
107
- ```
108
-
109
- Read it as: use the page's value; if there isn't one, use the site's.
110
-
111
- `noindex` and `canonical` work per-page only — there's no site-wide version,
112
- because "hide the whole site from search" and "one address for the whole site"
113
- aren't things you'd want.
114
-
115
- ## Theme
116
-
117
- One value in `site.json` controls the colour scheme everywhere:
118
-
119
- ```html
120
- <html data-theme="{{ site.theme }}" data-bs-theme="{{ site.theme }}">
121
- <meta name="color-scheme" content="{{ site.theme }}">
122
- <meta name="theme-color" content="{{ site.theme_color }}">
123
- ```
124
-
125
- | What it sets | Why it's there |
126
- |---|---|
127
- | `data-theme` | Your own hook — target it in your SCSS to define theme colours |
128
- | `data-bs-theme` | Bootstrap's dark mode switch. Harmlessly ignored with other frameworks |
129
- | `color-scheme` | Standard CSS — makes scrollbars, form fields and autofill match your theme |
130
- | `theme-color` | The colour of the browser bar on phones |
131
-
132
- Keep `theme` and `theme_color` in agreement: a dark theme with a white
133
- `theme_color` gives a white browser bar sitting on top of a dark page.
134
-
135
- See the **Styling with SCSS** DOC file for how to hook your own colours to
136
- `data-theme`.
137
-
138
- ## Favicon
139
-
140
- The favicon is the small icon in the browser tab. The three tags are written
141
- directly in `base.njk`. To change the icons, replace the files in
142
- `src/frontend/assets/brand/` **keeping the same names**:
143
-
144
- ```html
145
- <link rel="icon" type="image/svg+xml" href="{{ '/assets/brand/favicon.svg' | url }}">
146
- <link rel="icon" type="image/png" sizes="32x32" href="{{ '/assets/brand/favicon-32.png' | url }}">
147
- <link rel="apple-touch-icon" sizes="180x180" href="{{ '/assets/brand/apple-touch-icon.png' | url }}">
148
- ```
149
-
150
- | File | What uses it |
151
- |---|---|
152
- | `favicon.svg` | Modern browsers — scales to any size |
153
- | `favicon-32.png` | Older browsers and some crawlers |
154
- | `apple-touch-icon.png` | The icon iOS uses when someone saves your site to their home screen. Must be 180×180 and **not** transparent — iOS turns transparency into black |
155
-
156
- > ⚠️ Without `apple-touch-icon.png`, iOS uses a screenshot of your page as the
157
- > home screen icon instead.
158
-
159
- ## Where all this is written
160
-
161
- Everything above is produced by `src/frontend/layouts/base.njk`, which looks up
162
- the current page's entry like this:
163
-
164
- ```njk
165
- {% set pageKey = title %}
166
- {% set pageData = pages[pageKey] %}
167
- ```
168
-
169
- You only need to edit `base.njk` for something the boilerplate doesn't cover — a
170
- web font, an analytics snippet. For everything documented here, change the values
171
- in `site.json` and `pages.json` instead.
172
-
173
- > ⚠️ Paths in `base.njk` go through `| url`. That filter makes them work when the
174
- > site is published in a subfolder rather than at the root of a domain — GitHub
175
- > Pages being the common case. Remove it and every stylesheet and script breaks
176
- > there.
177
-
178
- ## Files for search engines and AI
179
-
180
- Three files are generated automatically and published with your site:
181
-
182
- | File | What it's for | Reachable at |
183
- |---|---|---|
184
- | `llms.txt` | Describes your site and lists its pages, for AI models | `yoursite.com/llms.txt` |
185
- | `robots.txt` | Tells search engines what they may and may not crawl | `yoursite.com/robots.txt` |
186
- | `sitemap.xml` | Lists your pages so search engines find them all | `yoursite.com/sitemap.xml` |
187
-
188
- They are built from templates in `src/frontend/indexing/`. `llms.txt` and
189
- `sitemap.xml` build their page lists the same way: they go through every page and
190
- leave out drafts, the 404 page, and anything marked `noindex`. Creating a page
191
- with the assistant is enough for it to appear in both — there is no list to keep
192
- up to date by hand.
193
-
194
- To change the wording or the fixed parts, edit
195
- `src/frontend/indexing/llms.njk` or `src/frontend/indexing/robots.njk`.
196
-
197
- > These three files must stay publicly reachable. If you block them in your server
198
- > config, search engines and AI models can't read them.
@@ -1,127 +0,0 @@
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 '../global.js';
15
-
16
- // import { initExampleModule } from '../modules/exampleModule.js';
17
-
18
- document.addEventListener("DOMContentLoaded", () => {
19
- // initExampleModule();
20
- });
21
-
22
- // Page logic here
23
- ```
24
-
25
- ## Global JS
26
-
27
- Some code has to run on every page: a header menu, a theme toggle, a cookie banner. Repeating it in every page entry point means you'll eventually forget it on one of them, and the bug only shows up on that single page.
28
-
29
- That's what `global.js` is for. It lives in `src/frontend/js/`, every page entry point imports it, and it takes care of running the shared modules.
30
-
31
- ### global.js <small>(`src/frontend/js/`)</small>
32
-
33
- ```js
34
- // import { initExampleModule } from './modules/exampleModule.js';
35
-
36
- function initGlobal() {
37
- // initExampleModule();
38
- }
39
-
40
- // Global logic here
41
-
42
- // Do not touch
43
- // This instruction starts global.js itself
44
- document.addEventListener('DOMContentLoaded', initGlobal);
45
- ```
46
-
47
- Pages import it without curly braces, since there is nothing to export — the module runs on its own as soon as it's part of the bundle:
48
-
49
- ```js
50
- import '../global.js';
51
- ```
52
-
53
- Note the difference in import paths. A page entry point sits inside `js/pages/`, so it reaches a module with `'../modules/exampleModule.js'`. `global.js` sits directly in `js/`, one level up, so the same module is `'./modules/exampleModule.js'`.
54
-
55
- ### A concrete example: the header menu
56
-
57
- A responsive header has a burger button that opens the navigation on small screens. The header is included in `base.njk`, so it's on every page — a textbook case for `global.js`.
58
-
59
- Write the behaviour as a normal module:
60
-
61
- ### header.js <small>(`src/frontend/js/modules/`)</small>
62
- ```js
63
- export function initHeader() {
64
- const toggle = document.querySelector('.menu-toggle');
65
- const menu = document.querySelector('.menu');
66
-
67
- if (!toggle || !menu) return;
68
-
69
- toggle.addEventListener('click', () => {
70
- menu.classList.toggle('is-open');
71
- });
72
- }
73
- ```
74
-
75
- Then wire it up once, in `global.js`:
76
-
77
- ```js
78
- import { initHeader } from './modules/header.js';
79
-
80
- function initGlobal() {
81
- initHeader();
82
- }
83
-
84
- // Do not touch
85
- // This instruction starts global.js itself
86
- document.addEventListener('DOMContentLoaded', initGlobal);
87
- ```
88
-
89
- The burger now works on every page, and no page entry point had to change. Adding another global behaviour later is two lines: one import, one call inside `initGlobal()`.
90
-
91
- The early `return` matters: if a page has no header, the module does nothing instead of throwing an error that would stop your other scripts.
92
-
93
- ### Global or page module?
94
-
95
- Use `global.js` for behaviour tied to something present on every page — anything included in `base.njk`, essentially.
96
-
97
- Keep it in the page entry point when only one or two pages need it. Everything imported by `global.js` ends up in *every* page bundle, so a heavy module used by a single page belongs in that page instead.
98
-
99
- ## Modules
100
-
101
- Modules live in `src/frontend/js/modules/`. Modules that interact with the DOM must be called inside `DOMContentLoaded`; others can be called anywhere.
102
-
103
- ## Adding a module
104
-
105
- Create a new `.js` file in `src/frontend/js/modules/`. Subfolders are allowed.
106
-
107
- Use ESM syntax — esbuild handles the bundling:
108
-
109
- ### exampleModule.js <small>(`src/frontend/js/modules/`)</small>
110
-
111
- ```js
112
- export function exampleModule() {
113
- // Module logic here
114
- }
115
- ```
116
-
117
- Then import it in the pages that need it:
118
-
119
- ```js
120
- import { exampleModule } from '../modules/exampleModule.js';
121
- ```
122
-
123
- In TypeScript, omit the extension:
124
-
125
- ```ts
126
- import { exampleModule } from '../modules/exampleModule';
127
- ```
@@ -1,66 +0,0 @@
1
- # Project Structure
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
- Nibula ships a fixed layout so you always know where to put your hands. Inside these folders you're free to organize as you like — subfolders in `components/`, `modules/` and `api/` are all fair game.
6
-
7
- ## The tree
8
-
9
- ```
10
- your-project/
11
- ├── .eleventy.js # Eleventy configuration
12
- ├── nginx.conf # nginx site file
13
- ├── package.json
14
- ├── out/ # Build output — never edit, never commit
15
- └── src/
16
- ├── backend/ # REST API — see docs/Backend.md
17
- └── frontend/
18
- ├── assets/ # Images, fonts, brand files
19
- │ └── brand/ # Favicons, logo etc
20
- ├── components/ # Reusable .njk fragments and .md content
21
- │ └── global/ # Header and footer, included in every page
22
- ├── data/ # site.json and pages.json
23
- ├── hosting/ # .htaccess and web.config
24
- ├── indexing/ # llms, robots and sitemap templates
25
- ├── js/ # global.js, modules/, pages/
26
- ├── layouts/ # base.njk — the HTML skeleton every page extends
27
- ├── routes/ # One file per page
28
- └── scss/ # _root.scss, _global.scss, modules/, pages/
29
- ```
30
-
31
- ## Where things happen
32
-
33
- | I want to... | Go to |
34
- |---|---|
35
- | Add a page | `nib cli` → creates the files in `routes/`, `scss/pages/`, `js/pages/` |
36
- | Write the content of a page | `routes/` |
37
- | Build a reusable block of markup | `components/` |
38
- | Write long prose in Markdown | `components/` — see docs/Components.md |
39
- | Change the header or footer | `components/global/` |
40
- | Style one page | `scss/pages/` |
41
- | Style every page | `scss/_global.scss` |
42
- | Define a variable used across the site | `scss/_root.scss` |
43
- | Run a script on one page | `js/pages/` |
44
- | Run a script everywhere | `js/global.js` |
45
- | Change the site name, URL, author | `data/site.json` |
46
- | Change a page's SEO | `data/pages.json` |
47
- | Edit the HTML `<head>` | `layouts/base.njk` |
48
- | Give one page a different skeleton | `layouts/` — add your own, see docs/Components.md |
49
- | Add an API endpoint | `backend/api/` |
50
-
51
- ## Two folders worth a note
52
-
53
- **`out/`** is generated by every build. Editing anything in it means losing the change the next time you run `nib run` or `nib build`. Always edit the source in `src/` — including `.htaccess` and `web.config`, whose sources live in `src/frontend/hosting/`.
54
-
55
- **`data/`** feeds the build but is never published: it holds the full page map, including pages you marked `noindex`, so it stays out of the output on purpose.
56
-
57
- ## What you can rearrange
58
-
59
- Subfolders are allowed wherever content is yours to organize:
60
-
61
- - `components/` — nest as deep as you like; includes take the path relative to `components/`, and so does the `markdownPath` filter
62
- - `scss/modules/` and `js/modules/` — same idea, remember to update the relative import paths
63
- - `layouts/` — extra layouts alongside `base.njk`; routes pick one by filename
64
- - `backend/api/public/` and `protected/` — subfolders become part of the URL
65
-
66
- The folders in the tree above are the part to leave alone: paths are wired into `.eleventy.js`, the build scripts and the assistant, so renaming one breaks the build.