@nera-static/plugin-navigation 2.0.1 → 2.2.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/CHANGELOG.md +91 -0
- package/README.md +105 -54
- package/bin/publish-template.js +18 -0
- package/config/navigation.yaml +30 -0
- package/index.js +35 -10
- package/package.json +10 -4
- package/views/helper/mixins.pug +1 -1
- package/views/helper/setup.pug +2 -2
- package/views/partials/link-list-navigation.pug +2 -2
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [2.2.0] - 2026-07-19
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- the `publish-template` command is now actually shipped. `bin/` was absent
|
|
13
|
+
from the published package and no `bin` entry existed, so the command
|
|
14
|
+
documented in the README could never resolve
|
|
15
|
+
- published templates now include their `partials/` and `helper/` files, so
|
|
16
|
+
they compile. Previously only the three top-level templates were copied and
|
|
17
|
+
every one of them failed on its first `include` (requires
|
|
18
|
+
`@nera-static/plugin-utils` ^1.2.0)
|
|
19
|
+
- a `navigation.yaml` entry missing `href` no longer crashes the build with a
|
|
20
|
+
`TypeError` from inside `node_modules`; the entry is skipped with a warning
|
|
21
|
+
naming its position
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
|
|
25
|
+
- a commented default `config/navigation.yaml` is now shipped
|
|
26
|
+
- `npx nera-navigation --force` re-publishes over existing templates,
|
|
27
|
+
discarding local edits
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
|
|
31
|
+
- `@nera-static/plugin-utils` range widened to `^1.2.0`
|
|
32
|
+
- navigation config is resolved per call rather than at module load
|
|
33
|
+
|
|
34
|
+
## [2.1.0] - 2024-12-27
|
|
35
|
+
|
|
36
|
+
### Added
|
|
37
|
+
|
|
38
|
+
- Professional CHANGELOG.md for release tracking
|
|
39
|
+
- Enhanced README.md with comprehensive documentation
|
|
40
|
+
- Support for Nera v4.1.0 static site generator
|
|
41
|
+
- Template publishing system via `bin/publish-template.js`
|
|
42
|
+
- BEM (Block Element Modifier) CSS methodology for all templates
|
|
43
|
+
- Comprehensive test suite with 14 tests covering all functionality
|
|
44
|
+
|
|
45
|
+
### Changed
|
|
46
|
+
|
|
47
|
+
- Updated development dependencies for security and performance
|
|
48
|
+
- Improved package.json metadata and repository references
|
|
49
|
+
- Enhanced code documentation and examples
|
|
50
|
+
- Modernized CSS classes using BEM methodology:
|
|
51
|
+
- `.nav`, `.nav__list`, `.nav__item`, `.nav__link`
|
|
52
|
+
- `.nav__link--active`, `.nav__link--active-path`
|
|
53
|
+
- `.nav--list`, `.nav__list--pipe-separated`
|
|
54
|
+
|
|
55
|
+
### Technical Details
|
|
56
|
+
|
|
57
|
+
- Maintains stable API with `getAppData()` function
|
|
58
|
+
- Full compatibility with Nera's plugin system
|
|
59
|
+
- Zero breaking changes from previous version
|
|
60
|
+
- All tests passing (14/14)
|
|
61
|
+
- Template publishing to `views/vendor/plugin-navigation/`
|
|
62
|
+
|
|
63
|
+
## [2.0.1] - Previous Release
|
|
64
|
+
|
|
65
|
+
### Note
|
|
66
|
+
|
|
67
|
+
- This version was already published on npm before our enhancements
|
|
68
|
+
|
|
69
|
+
## [2.0.0] - 2024-07-19
|
|
70
|
+
|
|
71
|
+
### Added
|
|
72
|
+
|
|
73
|
+
- Initial stable release for Nera static site generator
|
|
74
|
+
- Navigation generation from YAML configuration
|
|
75
|
+
- Support for multi-level navigations (main, footer, etc.)
|
|
76
|
+
- Built-in Pug templates and mixins
|
|
77
|
+
- Automatic active/path highlighting
|
|
78
|
+
- Comprehensive test coverage
|
|
79
|
+
|
|
80
|
+
### Features
|
|
81
|
+
|
|
82
|
+
- Single and multi-navigation support
|
|
83
|
+
- Flexible CSS class configuration
|
|
84
|
+
- Template inheritance and mixins
|
|
85
|
+
- Integration with @nera-static/plugin-utils
|
|
86
|
+
|
|
87
|
+
### Dependencies
|
|
88
|
+
|
|
89
|
+
- Node.js >=18 support
|
|
90
|
+
- ES modules architecture
|
|
91
|
+
- Modern development tooling
|
package/README.md
CHANGED
|
@@ -1,57 +1,67 @@
|
|
|
1
1
|
# @nera-static/plugin-navigation
|
|
2
2
|
|
|
3
|
-
A plugin for the [Nera](https://github.com/seebaermichi/nera) static site generator to create navigations from config files. Supports mixins and templates for easy rendering.
|
|
3
|
+
A plugin for the [Nera](https://github.com/seebaermichi/nera) static site generator to create navigations from config files. Supports mixins and templates for easy rendering and styling.
|
|
4
4
|
|
|
5
5
|
## ✨ Features
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
- Automatic active/path highlighting support
|
|
15
|
-
- Zero-runtime, static navigation structure
|
|
7
|
+
- Define one or more navigations via YAML config
|
|
8
|
+
- Access navigation data directly in templates (`app.nav`)
|
|
9
|
+
- Support for multi-level navigations (e.g., main, footer)
|
|
10
|
+
- Includes ready-to-use Pug templates and mixins
|
|
11
|
+
- Automatic active and path highlighting
|
|
12
|
+
- Static, zero-runtime overhead
|
|
13
|
+
- Full compatibility with Nera v4.1.0+
|
|
16
14
|
|
|
17
15
|
## 🚀 Installation
|
|
18
16
|
|
|
19
|
-
|
|
17
|
+
Install the plugin in your Nera project:
|
|
20
18
|
|
|
21
19
|
```bash
|
|
22
20
|
npm install @nera-static/plugin-navigation
|
|
23
21
|
```
|
|
24
22
|
|
|
25
|
-
Then create
|
|
23
|
+
Then create the configuration file in your project:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
mkdir -p config
|
|
27
|
+
touch config/navigation.yaml
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
This creates:
|
|
26
31
|
|
|
27
32
|
```
|
|
28
33
|
config/
|
|
29
34
|
└── navigation.yaml
|
|
30
35
|
```
|
|
31
36
|
|
|
32
|
-
|
|
37
|
+
The plugin ships a commented default at
|
|
38
|
+
`node_modules/@nera-static/plugin-navigation/config/navigation.yaml` — copy it
|
|
39
|
+
as a starting point. It is read from **your** project only; the packaged copy
|
|
40
|
+
is documentation, not a fallback.
|
|
41
|
+
|
|
42
|
+
Nera will automatically detect the plugin and load the configuration.
|
|
33
43
|
|
|
34
44
|
## ⚙️ Configuration
|
|
35
45
|
|
|
36
|
-
|
|
46
|
+
Define your navigation(s) in `config/navigation.yaml`.
|
|
37
47
|
|
|
38
|
-
|
|
48
|
+
### Single Navigation
|
|
39
49
|
|
|
40
50
|
```yaml
|
|
41
51
|
elements:
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
+
- href: /index.html
|
|
53
|
+
name: Home
|
|
54
|
+
- href: /service/service.html
|
|
55
|
+
name: Service
|
|
56
|
+
- href: /prices.html
|
|
57
|
+
name: Prices
|
|
58
|
+
- href: /contact.html
|
|
59
|
+
name: Contact
|
|
60
|
+
- href: /about-us/index.html
|
|
61
|
+
name: About us
|
|
52
62
|
```
|
|
53
63
|
|
|
54
|
-
Access
|
|
64
|
+
Access in templates:
|
|
55
65
|
|
|
56
66
|
```pug
|
|
57
67
|
app.nav.elements
|
|
@@ -61,67 +71,108 @@ app.nav.elements
|
|
|
61
71
|
|
|
62
72
|
```yaml
|
|
63
73
|
elements:
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
74
|
+
main:
|
|
75
|
+
- href: /index.html
|
|
76
|
+
name: Home
|
|
77
|
+
- href: /service/service.html
|
|
78
|
+
name: Service
|
|
79
|
+
footer:
|
|
80
|
+
- href: /imprint.html
|
|
81
|
+
name: Imprint
|
|
72
82
|
```
|
|
73
83
|
|
|
74
|
-
|
|
84
|
+
Access in templates:
|
|
75
85
|
|
|
76
86
|
```pug
|
|
77
87
|
app.nav.main.elements
|
|
78
88
|
app.nav.footer.elements
|
|
79
89
|
```
|
|
80
90
|
|
|
81
|
-
Each element
|
|
91
|
+
Each element includes `href`, `name`, and `path`.
|
|
82
92
|
|
|
83
|
-
## 🧩
|
|
93
|
+
## 🧩 Usage
|
|
84
94
|
|
|
85
|
-
###
|
|
95
|
+
### Manual Rendering
|
|
86
96
|
|
|
87
|
-
|
|
97
|
+
Loop through `app.nav.*.elements` directly in your Pug templates.
|
|
88
98
|
|
|
89
|
-
|
|
99
|
+
## 🛠️ Template Publishing
|
|
90
100
|
|
|
91
|
-
|
|
101
|
+
Use the default templates provided by the plugin:
|
|
92
102
|
|
|
93
|
-
```
|
|
94
|
-
|
|
103
|
+
```bash
|
|
104
|
+
npx nera-navigation
|
|
95
105
|
```
|
|
96
106
|
|
|
97
|
-
|
|
107
|
+
This copies every template file — including `partials/` and `helper/`, which
|
|
108
|
+
the top-level templates include — to:
|
|
98
109
|
|
|
99
|
-
|
|
100
|
-
-
|
|
110
|
+
```
|
|
111
|
+
views/vendor/plugin-navigation/
|
|
112
|
+
```
|
|
101
113
|
|
|
102
|
-
|
|
114
|
+
Then include them in your layouts or pages as needed.
|
|
103
115
|
|
|
104
|
-
|
|
116
|
+
Publishing **skips** if `views/vendor/plugin-navigation/` already exists, so
|
|
117
|
+
your edits are never overwritten. To pull in updated templates after a plugin
|
|
118
|
+
upgrade, discarding your changes to them:
|
|
105
119
|
|
|
106
|
-
```
|
|
107
|
-
|
|
120
|
+
```bash
|
|
121
|
+
npx nera-navigation --force
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## 🎨 Styling
|
|
125
|
+
|
|
126
|
+
The plugin uses BEM CSS methodology:
|
|
108
127
|
|
|
109
|
-
|
|
128
|
+
```css
|
|
129
|
+
.nav { }
|
|
130
|
+
.nav__list { }
|
|
131
|
+
.nav__item { }
|
|
132
|
+
.nav__item--inline { }
|
|
133
|
+
.nav__link { }
|
|
134
|
+
.nav__separator { }
|
|
135
|
+
.nav__link--active { }
|
|
136
|
+
.nav__link--active-path { }
|
|
110
137
|
```
|
|
111
138
|
|
|
112
|
-
|
|
139
|
+
Override or extend these classes in your project’s CSS.
|
|
140
|
+
|
|
141
|
+
## 📊 Generated Output
|
|
142
|
+
|
|
143
|
+
The plugin injects navigation data into `app.nav` without generating HTML. Use templates or mixins for output.
|
|
113
144
|
|
|
114
145
|
## 🧪 Development
|
|
115
146
|
|
|
116
147
|
```bash
|
|
117
148
|
npm install
|
|
118
|
-
npm
|
|
149
|
+
npm test
|
|
150
|
+
npm run lint
|
|
119
151
|
```
|
|
120
152
|
|
|
153
|
+
Tests use [Vitest](https://vitest.dev) and validate:
|
|
154
|
+
|
|
155
|
+
- Navigation data structure
|
|
156
|
+
- Multi-level navigation support
|
|
157
|
+
- Template rendering
|
|
158
|
+
- Class handling and active link detection
|
|
159
|
+
|
|
121
160
|
## 🧑💻 Author
|
|
122
161
|
|
|
123
162
|
Michael Becker
|
|
124
|
-
[
|
|
163
|
+
[https://github.com/seebaermichi](https://github.com/seebaermichi)
|
|
164
|
+
|
|
165
|
+
## 🔗 Links
|
|
166
|
+
|
|
167
|
+
- [Plugin Repository](https://github.com/seebaermichi/nera-plugin-navigation)
|
|
168
|
+
- [NPM Package](https://www.npmjs.com/package/@nera-static/plugin-navigation)
|
|
169
|
+
- [Nera Static Site Generator](https://github.com/seebaermichi/nera)
|
|
170
|
+
|
|
171
|
+
## 🧩 Compatibility
|
|
172
|
+
|
|
173
|
+
- **Nera**: v4.1.0+
|
|
174
|
+
- **Node.js**: >= 18
|
|
175
|
+
- **Plugin API**: Uses `getAppData()` for injecting navigation structure
|
|
125
176
|
|
|
126
177
|
## 📦 License
|
|
127
178
|
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import path from 'path'
|
|
4
|
+
import { fileURLToPath } from 'url'
|
|
5
|
+
import { publishAllTemplates } from '@nera-static/plugin-utils'
|
|
6
|
+
|
|
7
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
8
|
+
const pluginName = 'plugin-navigation'
|
|
9
|
+
const sourceDir = path.resolve(__dirname, '../views/')
|
|
10
|
+
const force = process.argv.includes('--force')
|
|
11
|
+
|
|
12
|
+
const result = publishAllTemplates({
|
|
13
|
+
pluginName,
|
|
14
|
+
sourceDir,
|
|
15
|
+
force,
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
process.exit(result ? 0 : 1)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Default configuration for @nera-static/plugin-navigation.
|
|
2
|
+
#
|
|
3
|
+
# This file is documentation and a starting point. The plugin reads
|
|
4
|
+
# config/navigation.yaml from YOUR project root, not from this package —
|
|
5
|
+
# copy this file there and edit it.
|
|
6
|
+
|
|
7
|
+
# CSS class applied to the link matching the current page.
|
|
8
|
+
active_class: active
|
|
9
|
+
|
|
10
|
+
# CSS class applied to a link whose path is an ancestor of the current page.
|
|
11
|
+
active_path_class: active-path
|
|
12
|
+
|
|
13
|
+
# Base CSS class for the navigation element.
|
|
14
|
+
nav_class: nav
|
|
15
|
+
|
|
16
|
+
# A single navigation: a flat list, available as app.nav.elements
|
|
17
|
+
elements:
|
|
18
|
+
- href: /index.html
|
|
19
|
+
name: Home
|
|
20
|
+
|
|
21
|
+
# For several navigations, nest them under names instead. Each becomes
|
|
22
|
+
# app.nav.<name>.elements:
|
|
23
|
+
#
|
|
24
|
+
# elements:
|
|
25
|
+
# main:
|
|
26
|
+
# - href: /index.html
|
|
27
|
+
# name: Home
|
|
28
|
+
# footer:
|
|
29
|
+
# - href: /imprint.html
|
|
30
|
+
# name: Imprint
|
package/index.js
CHANGED
|
@@ -1,18 +1,43 @@
|
|
|
1
1
|
import path from 'path'
|
|
2
2
|
import { getConfig } from '@nera-static/plugin-utils'
|
|
3
3
|
|
|
4
|
-
//
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
4
|
+
// Resolved per call rather than at module scope: the host project's cwd is
|
|
5
|
+
// what matters, and resolving lazily is what makes this testable.
|
|
6
|
+
function getHostConfigPath() {
|
|
7
|
+
return path.resolve(process.cwd(), 'config/navigation.yaml')
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function getNavElements(elements, groupName) {
|
|
11
|
+
if (!Array.isArray(elements)) {
|
|
12
|
+
console.warn(
|
|
13
|
+
`⚠️ navigation.yaml: expected a list of elements${groupName ? ` under "${groupName}"` : ''}, got ${typeof elements}. Skipping.`
|
|
14
|
+
)
|
|
15
|
+
return []
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return elements
|
|
19
|
+
.filter((element, index) => {
|
|
20
|
+
// A missing href is an ordinary YAML typo. Without this guard
|
|
21
|
+
// path.posix.dirname throws a TypeError from inside node_modules,
|
|
22
|
+
// which tells the user nothing about which entry is at fault.
|
|
23
|
+
if (!element || typeof element.href !== 'string') {
|
|
24
|
+
const where = groupName ? `${groupName}[${index}]` : `elements[${index}]`
|
|
25
|
+
console.warn(
|
|
26
|
+
`⚠️ navigation.yaml: skipping ${where} — it has no "href".`
|
|
27
|
+
)
|
|
28
|
+
return false
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return true
|
|
32
|
+
})
|
|
33
|
+
.map((element) => ({
|
|
34
|
+
...element,
|
|
35
|
+
path: path.posix.dirname(element.href),
|
|
36
|
+
}))
|
|
12
37
|
}
|
|
13
38
|
|
|
14
39
|
function getMainNav() {
|
|
15
|
-
const navConfig = getConfig(
|
|
40
|
+
const navConfig = getConfig(getHostConfigPath())
|
|
16
41
|
|
|
17
42
|
navConfig.activeClass = navConfig.active_class || 'active'
|
|
18
43
|
navConfig.activePathClass = navConfig.active_path_class || 'active-path'
|
|
@@ -24,7 +49,7 @@ function getMainNav() {
|
|
|
24
49
|
for (const key in navConfig.elements) {
|
|
25
50
|
navConfig[key] = {
|
|
26
51
|
className: `${key}-${navConfig.navClass}`,
|
|
27
|
-
elements: getNavElements(navConfig.elements[key]),
|
|
52
|
+
elements: getNavElements(navConfig.elements[key], key),
|
|
28
53
|
}
|
|
29
54
|
}
|
|
30
55
|
}
|
package/package.json
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nera-static/plugin-navigation",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "A plugin for Nera static site generator to generate navigations with optional templates and mixins.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
7
|
+
"publish-template": "node ./bin/publish-template.js",
|
|
7
8
|
"lint": "eslint .",
|
|
8
9
|
"test": "vitest"
|
|
9
10
|
},
|
|
@@ -11,10 +12,15 @@
|
|
|
11
12
|
"exports": {
|
|
12
13
|
".": "./index.js"
|
|
13
14
|
},
|
|
15
|
+
"bin": {
|
|
16
|
+
"nera-navigation": "./bin/publish-template.js"
|
|
17
|
+
},
|
|
14
18
|
"files": [
|
|
19
|
+
"bin",
|
|
15
20
|
"config",
|
|
16
21
|
"index.js",
|
|
17
|
-
"views"
|
|
22
|
+
"views",
|
|
23
|
+
"CHANGELOG.md"
|
|
18
24
|
],
|
|
19
25
|
"keywords": [
|
|
20
26
|
"nera",
|
|
@@ -35,14 +41,14 @@
|
|
|
35
41
|
},
|
|
36
42
|
"homepage": "https://github.com/seebaermichi/nera-plugin-navigation#readme",
|
|
37
43
|
"dependencies": {
|
|
38
|
-
"@nera-static/plugin-utils": "^1.0
|
|
44
|
+
"@nera-static/plugin-utils": "^1.2.0"
|
|
39
45
|
},
|
|
40
46
|
"engines": {
|
|
41
47
|
"node": ">=18"
|
|
42
48
|
},
|
|
43
49
|
"devDependencies": {
|
|
44
50
|
"cheerio": "^1.1.0",
|
|
45
|
-
"eslint": "^9.
|
|
51
|
+
"eslint": "^9.17.0",
|
|
46
52
|
"husky": "^9.1.7",
|
|
47
53
|
"pug": "^3.0.3",
|
|
48
54
|
"vitest": "^3.2.4"
|
package/views/helper/mixins.pug
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
mixin link(item, activeClass, activePathClass)
|
|
2
|
-
a(href=item.href, class=`${activeClass}${activePathClass}
|
|
2
|
+
a.nav__link(href=item.href, class=`${activeClass} ${activePathClass}`.trim()) #{ item.name }
|
package/views/helper/setup.pug
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
each item, index in elements || app.nav.elements
|
|
2
2
|
- const isActive = item.href === meta.fullPath
|
|
3
|
-
- const activeClass = isActive ?
|
|
3
|
+
- const activeClass = isActive ? 'nav__link--active' : ''
|
|
4
4
|
- const isNotHome = meta.dirname !== '/'
|
|
5
5
|
- const isParent = isNotHome && item.href.indexOf(meta.dirname) === 0
|
|
6
|
-
- const activePathClass = !isActive && isParent ?
|
|
6
|
+
- const activePathClass = !isActive && isParent ? 'nav__link--active-path' : ''
|