@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 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
- - Define one or more navigations via YAML config
8
- - Access navigation data in your templates (`app.nav`)
9
- - Support for multi-level navigations (e.g. main, footer)
10
- - Includes built-in Pug templates and mixins for rendering:
11
- - Pipe-separated links
12
- - Link lists
13
- - Mixin-powered flexible layout
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
- You can install this plugin by running the following in the root folder of your Nera project:
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 a `navigation.yaml` file inside your project’s `config/` directory:
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
- Nera will automatically detect the plugin and load your navigation configuration. No additional setup or imports are required.
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
- ### Single Navigation
46
+ Define your navigation(s) in `config/navigation.yaml`.
37
47
 
38
- Example `navigation/config/navigation.yaml`:
48
+ ### Single Navigation
39
49
 
40
50
  ```yaml
41
51
  elements:
42
- - href: /index.html
43
- name: Home
44
- - href: /service/service.html
45
- name: Service
46
- - href: /prices.html
47
- name: Prices
48
- - href: /contact.html
49
- name: Contact
50
- - href: /about-us/index.html
51
- name: About us
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 it in your templates via:
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
- main:
65
- - href: /index.html
66
- name: Home
67
- - href: /service/service.html
68
- name: Service
69
- footer:
70
- - href: /imprint.html
71
- name: Imprint
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
- Use:
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 has: `href`, `name`, `path`.
91
+ Each element includes `href`, `name`, and `path`.
82
92
 
83
- ## 🧩 Rendering Options
93
+ ## 🧩 Usage
84
94
 
85
- ### Custom Rendering
95
+ ### Manual Rendering
86
96
 
87
- Use the `app.nav.*.elements` arrays in your templates manually or via your own loops.
97
+ Loop through `app.nav.*.elements` directly in your Pug templates.
88
98
 
89
- ### Built-in Templates
99
+ ## 🛠️ Template Publishing
90
100
 
91
- If you like to, include one of the built-in templates in `views/`:
101
+ Use the default templates provided by the plugin:
92
102
 
93
- ```pug
94
- include ../../src/plugins/navigation/views/simple-navigation
103
+ ```bash
104
+ npx nera-navigation
95
105
  ```
96
106
 
97
- Other available templates:
107
+ This copies every template file — including `partials/` and `helper/`, which
108
+ the top-level templates include — to:
98
109
 
99
- - `pipe-separated-navigation.pug`
100
- - `link-list-navigation.pug`
110
+ ```
111
+ views/vendor/plugin-navigation/
112
+ ```
101
113
 
102
- ### Built-in Mixins
114
+ Then include them in your layouts or pages as needed.
103
115
 
104
- If using multiple navigations, use the mixins in `views/mixins/`:
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
- ```pug
107
- include ../../src/plugins/navigation/views/mixins/pipe-separated-navigation
120
+ ```bash
121
+ npx nera-navigation --force
122
+ ```
123
+
124
+ ## 🎨 Styling
125
+
126
+ The plugin uses BEM CSS methodology:
108
127
 
109
- +pipeSeparatedNav(app.nav.main.elements, app.nav.main.className)
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
- > The optional `nav_class` value can be set in the YAML config.
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 run test
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
- [GitHub](https://github.com/seebaermichi)
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
- // Default location to look for navigation.yaml in host project
5
- const HOST_CONFIG_PATH = path.resolve(process.cwd(), 'config/navigation.yaml')
6
-
7
- function getNavElements(elements) {
8
- return elements.map((element) => ({
9
- ...element,
10
- path: path.posix.dirname(element.href),
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(HOST_CONFIG_PATH)
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.1",
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.3"
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.31.0",
51
+ "eslint": "^9.17.0",
46
52
  "husky": "^9.1.7",
47
53
  "pug": "^3.0.3",
48
54
  "vitest": "^3.2.4"
@@ -1,2 +1,2 @@
1
1
  mixin link(item, activeClass, activePathClass)
2
- a(href=item.href, class=`${activeClass}${activePathClass}`) #{ item.name }
2
+ a.nav__link(href=item.href, class=`${activeClass} ${activePathClass}`.trim()) #{ item.name }
@@ -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 ? app.nav.activeClass : ''
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 ? app.nav.activePathClass : ''
6
+ - const activePathClass = !isActive && isParent ? 'nav__link--active-path' : ''
@@ -1,7 +1,7 @@
1
1
  include ../helper/mixins
2
2
 
3
3
  mixin linkListNav(elements=app.nav.elements, className='')
4
- ul.nav(class=`${className}`)
4
+ ul.nav.nav--list(class=`${className}`)
5
5
  include ../helper/setup
6
- li.nav-item
6
+ li.nav__item
7
7
  +link(item, activeClass, activePathClass)