@nera-static/nera 1.8.0 → 1.9.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/README.md +33 -3
- package/bin/nera.js +6 -5
- package/package.json +1 -1
- package/src/commands/build.js +13 -2
- package/src/commands/check.js +5 -2
- package/src/commands/validate.js +5 -2
- package/src/json-output.js +57 -0
- package/template/AGENTS.md +4 -0
package/README.md
CHANGED
|
@@ -28,12 +28,12 @@ package is named after the folder (`My Bakery` → `my-bakery`).
|
|
|
28
28
|
| Command | What it does |
|
|
29
29
|
|---|---|
|
|
30
30
|
| `nera new <name>` | Scaffold a new thin Nera site (one dependency: `@nera-static/nera`). Add `--theme <name>` to start from a theme. `nera new .` scaffolds into the current, empty folder (dotfiles, `AGENTS.md` and `CLAUDE.md` allowed). |
|
|
31
|
-
| `nera build` | Render `pages/` → `public/`. With `--check`, then run `nera check` on the result — one command for CI. |
|
|
31
|
+
| `nera build` | Render `pages/` → `public/`. With `--check`, then run `nera check` on the result — one command for CI. `--json` prints the result as one JSON object (see [Machine-readable output](#machine-readable-output)). |
|
|
32
32
|
| `nera dev` | Build, serve `public/`, and rebuild on change with live reload. Watches `pages/`, `config/`, `theme/` and a local theme (`theme: ./themes/<name>`). |
|
|
33
33
|
| `nera serve` | Serve the already-built `public/` folder. |
|
|
34
34
|
| `nera update` | Update the site's Nera packages, then create a missing `AGENTS.md`/`CLAUDE.md` and refresh the Nera part of `AGENTS.md` (see [Instructions for AI assistants](#instructions-for-ai-assistants)). On a legacy cloned site, `nera update --migrate` converts it to the thin model. |
|
|
35
|
-
| `nera validate` | Check the site's sources (layouts, includes, YAML) before publishing; exits non-zero on any error. |
|
|
36
|
-
| `nera check` | Check the built `public/` for accessibility, privacy and legal-notice problems (see below). Build first. |
|
|
35
|
+
| `nera validate` | Check the site's sources (layouts, includes, YAML) before publishing; exits non-zero on any error. `--json` for machine-readable output. |
|
|
36
|
+
| `nera check` | Check the built `public/` for accessibility, privacy and legal-notice problems (see below). Build first. `--json` for machine-readable output. |
|
|
37
37
|
|
|
38
38
|
A scaffolded site is a thin project — no vendored engine, no clone. It lists one
|
|
39
39
|
dependency and its scripts call the CLI:
|
|
@@ -161,6 +161,36 @@ links are found by their text in German and English only (*Impressum*,
|
|
|
161
161
|
WCAG criterion or law behind each rule, are in the
|
|
162
162
|
[`@nera-static/validate` README](https://github.com/seebaermichi/nera-validate#checking-the-built-output).
|
|
163
163
|
|
|
164
|
+
## Machine-readable output
|
|
165
|
+
|
|
166
|
+
`nera validate --json`, `nera check --json` and `nera build --check --json`
|
|
167
|
+
print exactly one JSON object on stdout and nothing else, for an AI agent or a
|
|
168
|
+
script to parse:
|
|
169
|
+
|
|
170
|
+
```json
|
|
171
|
+
{
|
|
172
|
+
"ok": true,
|
|
173
|
+
"results": [
|
|
174
|
+
{
|
|
175
|
+
"file": "public/index.html",
|
|
176
|
+
"line": 26,
|
|
177
|
+
"severity": "warning",
|
|
178
|
+
"rule": "a11y-skip-link",
|
|
179
|
+
"message": "the first focusable element is not a skip link — …"
|
|
180
|
+
}
|
|
181
|
+
]
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
`results` is the validator's list as it is, in the same order (`severity` is
|
|
186
|
+
`error` or `warning`); `ok` is `true` exactly when the exit code is `0`, so
|
|
187
|
+
warnings alone keep it `true`. When the command cannot run — no `public/` for
|
|
188
|
+
`nera check`, a failed build, a config the validator cannot read — stdout holds
|
|
189
|
+
`{ "ok": false, "error": "<message>", "results": [] }` and the exit code is `1`.
|
|
190
|
+
Every log line (the build log, plugin output) goes to stderr, and the exit code
|
|
191
|
+
is the same as without `--json`. `nera build --json` without `--check` prints
|
|
192
|
+
`{ "ok": true, "results": [] }` after a successful build.
|
|
193
|
+
|
|
164
194
|
## Migrating a cloned (legacy) site
|
|
165
195
|
|
|
166
196
|
Older Nera sites were git clones that vendored the engine under `src/`. Inside
|
package/bin/nera.js
CHANGED
|
@@ -8,18 +8,19 @@ import { runValidate } from '../src/commands/validate.js'
|
|
|
8
8
|
import { runCheck } from '../src/commands/check.js'
|
|
9
9
|
|
|
10
10
|
const [, , command, ...rest] = process.argv
|
|
11
|
+
const json = rest.includes('--json')
|
|
11
12
|
|
|
12
13
|
const usage = `Nera — the static site generator CLI
|
|
13
14
|
|
|
14
15
|
Usage:
|
|
15
16
|
nera new <name> scaffold a new Nera site (--theme <name> to start from a theme)
|
|
16
17
|
nera new . scaffold into the current, empty folder
|
|
17
|
-
nera build render pages/ → public/ (--check to check the output after)
|
|
18
|
+
nera build render pages/ → public/ (--check to check the output after, --json for machine-readable output)
|
|
18
19
|
nera dev build, serve and live-reload
|
|
19
20
|
nera serve serve the built public/ folder
|
|
20
21
|
nera update update Nera packages (or migrate a cloned site with --migrate)
|
|
21
|
-
nera validate check the site (layouts, includes, YAML) before publish
|
|
22
|
-
nera check check the built public/ (accessibility, privacy, legal hints)
|
|
22
|
+
nera validate check the site (layouts, includes, YAML) before publish (--json for machine-readable output)
|
|
23
|
+
nera check check the built public/ (accessibility, privacy, legal hints) (--json for machine-readable output)
|
|
23
24
|
`
|
|
24
25
|
|
|
25
26
|
try {
|
|
@@ -40,10 +41,10 @@ try {
|
|
|
40
41
|
await runUpdate(rest)
|
|
41
42
|
break
|
|
42
43
|
case 'validate':
|
|
43
|
-
process.
|
|
44
|
+
process.exitCode = runValidate({ json })
|
|
44
45
|
break
|
|
45
46
|
case 'check':
|
|
46
|
-
process.
|
|
47
|
+
process.exitCode = runCheck({ json })
|
|
47
48
|
break
|
|
48
49
|
default:
|
|
49
50
|
console.log(usage)
|
package/package.json
CHANGED
package/src/commands/build.js
CHANGED
|
@@ -1,12 +1,23 @@
|
|
|
1
1
|
import run from '@nera-static/core'
|
|
2
|
+
import { validateOutput } from '@nera-static/validate'
|
|
2
3
|
import { runCheck } from './check.js'
|
|
4
|
+
import { runJson } from '../json-output.js'
|
|
3
5
|
|
|
4
6
|
// Render the current site (pages/ → public/) using the shared engine. `run`
|
|
5
7
|
// reads config and folders relative to the current working directory, so this
|
|
6
8
|
// builds whichever site the CLI is invoked in. With `--check`, the fresh output
|
|
7
9
|
// is then checked as by `nera check` — one command for CI. Returns the exit
|
|
8
|
-
// code: the check's, or 0 for a plain build.
|
|
10
|
+
// code: the check's, or 0 for a plain build. With `--json`, the build log goes
|
|
11
|
+
// to stderr and stdout holds one object: the check's `{ ok, results }`, empty
|
|
12
|
+
// results without `--check`, or the failure (see json-output.js).
|
|
9
13
|
export async function runBuild(args = []) {
|
|
14
|
+
const check = args.includes('--check')
|
|
15
|
+
if (args.includes('--json')) {
|
|
16
|
+
return runJson(async () => {
|
|
17
|
+
await run()
|
|
18
|
+
return check ? validateOutput({ cwd: process.cwd() }) : []
|
|
19
|
+
})
|
|
20
|
+
}
|
|
10
21
|
await run()
|
|
11
|
-
return
|
|
22
|
+
return check ? runCheck() : 0
|
|
12
23
|
}
|
package/src/commands/check.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { validateOutput, hasErrors, formatOutputResults } from '@nera-static/validate'
|
|
2
|
+
import { runJson } from '../json-output.js'
|
|
2
3
|
|
|
3
4
|
// Check the built output in public/ — accessibility, privacy and legal-notice
|
|
4
5
|
// hints — and print the results. `nera validate` checks the sources; this checks
|
|
5
6
|
// what the build produced, so it needs a build first: validateOutput throws
|
|
6
7
|
// "run `nera build` first" when public/ is missing or holds no HTML. Returns the
|
|
7
|
-
// exit code (1 on any error, 0 otherwise), like runValidate.
|
|
8
|
-
|
|
8
|
+
// exit code (1 on any error, 0 otherwise), like runValidate. With `json`, it
|
|
9
|
+
// prints one JSON object instead, the error included (see json-output.js).
|
|
10
|
+
export function runCheck({ cwd = process.cwd(), json = false } = {}) {
|
|
11
|
+
if (json) return runJson(() => validateOutput({ cwd }))
|
|
9
12
|
const results = validateOutput({ cwd })
|
|
10
13
|
console.log(formatOutputResults(results))
|
|
11
14
|
return hasErrors(results) ? 1 : 0
|
package/src/commands/validate.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { validateSite, hasErrors, formatResults } from '@nera-static/validate'
|
|
2
|
+
import { runJson } from '../json-output.js'
|
|
2
3
|
|
|
3
4
|
// Validate the current site and print the results. Returns the process exit code
|
|
4
5
|
// (1 on any error, 0 otherwise) rather than calling process.exit itself, so it
|
|
5
|
-
// stays testable; the bin turns the return value into the exit.
|
|
6
|
-
|
|
6
|
+
// stays testable; the bin turns the return value into the exit. With `json`, it
|
|
7
|
+
// prints one `{ ok, results }` object instead (see json-output.js).
|
|
8
|
+
export function runValidate({ cwd = process.cwd(), json = false } = {}) {
|
|
9
|
+
if (json) return runJson(() => validateSite({ cwd }))
|
|
7
10
|
const results = validateSite({ cwd })
|
|
8
11
|
console.log(formatResults(results))
|
|
9
12
|
return hasErrors(results) ? 1 : 0
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { hasErrors } from '@nera-static/validate'
|
|
2
|
+
|
|
3
|
+
// `--json` mode, shared by `nera validate`, `nera check` and `nera build`: stdout
|
|
4
|
+
// holds exactly one JSON object and nothing else, so an agent can parse it whole.
|
|
5
|
+
// While `body` runs, console.log/info/debug and raw stdout writes (the engine's
|
|
6
|
+
// build log, plugin output) go to stderr; they are restored afterwards. `body`
|
|
7
|
+
// returns the validator's results, directly or as a promise. The object is
|
|
8
|
+
// `{ ok, results }`, or `{ ok: false, error, results: [] }` when `body` throws —
|
|
9
|
+
// the error is reported here, not rethrown. Returns the exit code, the same one
|
|
10
|
+
// text mode ends with: 1 on any error, 0 otherwise. A sync body keeps the call
|
|
11
|
+
// sync.
|
|
12
|
+
export function runJson(body) {
|
|
13
|
+
const restore = routeLogsToStderr()
|
|
14
|
+
const finish = (value) => {
|
|
15
|
+
restore()
|
|
16
|
+
process.stdout.write(`${JSON.stringify(value, null, 2)}\n`)
|
|
17
|
+
return value.ok ? 0 : 1
|
|
18
|
+
}
|
|
19
|
+
const report = (results) => ({ ok: !hasErrors(results), results })
|
|
20
|
+
const failure = (error) => ({ ok: false, error: error.message, results: [] })
|
|
21
|
+
|
|
22
|
+
let pending
|
|
23
|
+
try {
|
|
24
|
+
pending = body()
|
|
25
|
+
} catch (error) {
|
|
26
|
+
return finish(failure(error))
|
|
27
|
+
}
|
|
28
|
+
if (typeof pending?.then === 'function') {
|
|
29
|
+
return pending.then(
|
|
30
|
+
(results) => finish(report(results)),
|
|
31
|
+
(error) => finish(failure(error))
|
|
32
|
+
)
|
|
33
|
+
}
|
|
34
|
+
return finish(report(pending))
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Point console.log/info/debug and process.stdout.write at stderr. Returns the
|
|
38
|
+
// function that puts the originals back.
|
|
39
|
+
function routeLogsToStderr() {
|
|
40
|
+
const saved = {
|
|
41
|
+
log: console.log,
|
|
42
|
+
info: console.info,
|
|
43
|
+
debug: console.debug,
|
|
44
|
+
write: process.stdout.write,
|
|
45
|
+
}
|
|
46
|
+
const toStderr = (...args) => console.error(...args)
|
|
47
|
+
console.log = toStderr
|
|
48
|
+
console.info = toStderr
|
|
49
|
+
console.debug = toStderr
|
|
50
|
+
process.stdout.write = (...args) => process.stderr.write(...args)
|
|
51
|
+
return () => {
|
|
52
|
+
console.log = saved.log
|
|
53
|
+
console.info = saved.info
|
|
54
|
+
console.debug = saved.debug
|
|
55
|
+
process.stdout.write = saved.write
|
|
56
|
+
}
|
|
57
|
+
}
|
package/template/AGENTS.md
CHANGED
|
@@ -35,6 +35,10 @@ Markdown file.
|
|
|
35
35
|
- `npx nera build --check` — build, then check the built `public/` for
|
|
36
36
|
accessibility, privacy and legal issues (`npx nera check` checks without
|
|
37
37
|
building)
|
|
38
|
+
- `npx nera validate --json`, `npx nera check --json` and
|
|
39
|
+
`npx nera build --check --json` print one JSON object on stdout,
|
|
40
|
+
`{ "ok", "results": [{ file, line, severity, rule, message }] }`, with
|
|
41
|
+
the same exit code; the build log goes to stderr
|
|
38
42
|
|
|
39
43
|
## After every change
|
|
40
44
|
|