@kirigami/php-prepros 1.9.2 → 2.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/README.md +145 -74
- package/package.json +3 -3
- package/src/libraries/ld.class.php +24 -21
- package/src/libraries/meta.class.php +18 -15
- package/src/prepros.js +3 -2
package/README.md
CHANGED
|
@@ -12,6 +12,7 @@ PHP preprocessor for the **Kirigami** static site generator.
|
|
|
12
12
|
[](https://www.npmjs.com/package/@kirigami/php-prepros)
|
|
13
13
|
[](./LICENSE)
|
|
14
14
|
[](https://nodejs.org)
|
|
15
|
+
[](https://php-kirigami.github.io)
|
|
15
16
|
|
|
16
17
|
</div>
|
|
17
18
|
|
|
@@ -34,6 +35,12 @@ Part of the **Kirigami** project ecosystem.
|
|
|
34
35
|
- [@kirigami/php-prepros](#kirigamiphp-prepros)
|
|
35
36
|
- [Overview](#overview)
|
|
36
37
|
- [Table of contents](#table-of-contents)
|
|
38
|
+
- [What's new in 2.0.0](#whats-new-in-200)
|
|
39
|
+
- [What's new in 1.9.3](#whats-new-in-193)
|
|
40
|
+
- [What's new in 1.9.2](#whats-new-in-192)
|
|
41
|
+
- [What's new in 1.9.1](#whats-new-in-191)
|
|
42
|
+
- [What's new in 1.9.0](#whats-new-in-190)
|
|
43
|
+
- [What's new in 1.8.0](#whats-new-in-180)
|
|
37
44
|
- [What's new in 1.7.2](#whats-new-in-172)
|
|
38
45
|
- [What's new in 1.7.1](#whats-new-in-171)
|
|
39
46
|
- [What's new in 1.7.0](#whats-new-in-170)
|
|
@@ -46,8 +53,7 @@ Part of the **Kirigami** project ecosystem.
|
|
|
46
53
|
- [Installation](#installation)
|
|
47
54
|
- [Configuration — `kirigami.yaml`](#configuration--kirigamiyaml)
|
|
48
55
|
- [`kirigami` block](#kirigami-block)
|
|
49
|
-
- [`
|
|
50
|
-
- [`meta` block](#meta-block)
|
|
56
|
+
- [`seo` block](#seo-block)
|
|
51
57
|
- [`prepros` block](#prepros-block)
|
|
52
58
|
- [`image` block](#image-block)
|
|
53
59
|
- [`plugins` block](#plugins-block)
|
|
@@ -109,6 +115,60 @@ Part of the **Kirigami** project ecosystem.
|
|
|
109
115
|
|
|
110
116
|
---
|
|
111
117
|
|
|
118
|
+
## What's new in 2.0.0
|
|
119
|
+
|
|
120
|
+
- **Breaking: `meta:` and `jsonld:` merged into one top-level `seo:` block.**
|
|
121
|
+
The two used to be independent siblings of `kirigami:` that happened to
|
|
122
|
+
share fallback data; they're now one block, one mental model for a
|
|
123
|
+
project's whole SEO/social surface — `META`'s own keys live directly under
|
|
124
|
+
`seo:`, and `jsonld` is nested inside it as its own sub-block:
|
|
125
|
+
|
|
126
|
+
```yaml
|
|
127
|
+
# before (1.x)
|
|
128
|
+
meta:
|
|
129
|
+
favicon: favicon.png
|
|
130
|
+
jsonld: {}
|
|
131
|
+
|
|
132
|
+
# after (2.0.0)
|
|
133
|
+
seo:
|
|
134
|
+
favicon: favicon.png
|
|
135
|
+
jsonld: {}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
The two stay **independently toggled** exactly as before — a project can
|
|
139
|
+
have META's tags without JSON-LD, or vice versa; `seo: { jsonld: false }`
|
|
140
|
+
(or `{ auto: false }`) stops just the JSON-LD injection, `seo: false` (or
|
|
141
|
+
`{ auto: false }` at the top level) stops just META's. Every fallback chain
|
|
142
|
+
(a page's PHPDOC → the block → the loose `kirigami:` keys) is unchanged,
|
|
143
|
+
including `jsonld`'s own keys still feeding META's defaults (`jsonld.image`,
|
|
144
|
+
`jsonld.lang`, `jsonld.person`, …) — only *where the two blocks live* in
|
|
145
|
+
`kirigami.yaml` changed, not how resolution works. **Migration**: rename
|
|
146
|
+
`meta:` to `seo:` and move the `jsonld:` block's content under it as
|
|
147
|
+
`seo.jsonld:`. See [`seo` block](#seo-block) / [`meta` config](#meta-config)
|
|
148
|
+
/ [`jsonld` config](#jsonld-config).
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## What's new in 1.9.3
|
|
153
|
+
|
|
154
|
+
- Dependency bump to `@kirigami/struct-walker` 1.0.5; `homepage` + README
|
|
155
|
+
pointed at the site (metadata only).
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## What's new in 1.9.2
|
|
160
|
+
|
|
161
|
+
- **Fixed a real Markdown bug**: a list item's source line count was 1:1
|
|
162
|
+
with `<li>` count, so an indented continuation line with no marker of its
|
|
163
|
+
own (a soft-wrapped `- **foo** text\n more text`) fell outside the
|
|
164
|
+
block-matching regex entirely — the list closed after the first line, the
|
|
165
|
+
continuation resurfaced as a stray flat `<p>`, and a new list reopened for
|
|
166
|
+
the next marker line. Fixed by widening the block regex to also accept a
|
|
167
|
+
marker-less indented line and, in the per-line loop, appending it to the
|
|
168
|
+
previous item instead of dropping it.
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
112
172
|
## What's new in 1.9.1
|
|
113
173
|
|
|
114
174
|
- **`{% img-asset %}` no longer crashes the build on an unresolvable path.**
|
|
@@ -376,7 +436,7 @@ npm install @kirigami/php-prepros
|
|
|
376
436
|
|
|
377
437
|
Every project **must** have a `kirigami.yaml` at its root. The preprocessor reads it at startup and throws if it is absent or invalid.
|
|
378
438
|
|
|
379
|
-
`@kirigami/php-prepros` itself only acts on four blocks — **`kirigami:`**, **`
|
|
439
|
+
`@kirigami/php-prepros` itself only acts on four blocks — **`kirigami:`**, **`seo:`**, **`prepros:`**, and **`image:`**. The remaining blocks (**`plugins:`**, **`esbuild:`**, **`sass:`**, **`export:`**, **`scripts:`**, **`tasks:`**) are consumed by the [`kiri`](https://www.npmjs.com/package/@kirigami/kirigami) CLI that drives the build; they are documented here for completeness because everything lives in the one file. The full file is validated against [`kirigami.schema.json`](https://github.com/php-kirigami/kirigami/blob/main/packages/kirigami/kirigami.schema.json), also served for editor autocompletion:
|
|
380
440
|
|
|
381
441
|
```yaml
|
|
382
442
|
# yaml-language-server: $schema=https://cdn.jsdelivr.net/gh/php-kirigami/kirigami@main/packages/kirigami/kirigami.schema.json
|
|
@@ -404,9 +464,10 @@ kirigami:
|
|
|
404
464
|
- keyword one
|
|
405
465
|
- keyword two
|
|
406
466
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
467
|
+
seo: # Presence turns on the META <head> tags generator.
|
|
468
|
+
jsonld: # Nested, independent opt-in for the LD JSON-LD generator.
|
|
469
|
+
type: Organization # `jsonld: {}` alone is enough; see the seo block below.
|
|
470
|
+
logo: assets/logo.png
|
|
410
471
|
|
|
411
472
|
prepros:
|
|
412
473
|
before: _layouts/header.php # Included before every page body.
|
|
@@ -466,30 +527,31 @@ Core project settings. **Read by `php-prepros`.** The entire block is extracted
|
|
|
466
527
|
| `baseurl` | ✅ | Root URL of the deployed site, no trailing slash. Used to build absolute `<loc>` entries in `sitemap.xml`; exposed as `$baseurl`. |
|
|
467
528
|
| `root` | ✅ | Path (relative to the project root) to the directory containing your `_*.php` source pages. Build fails immediately if missing or if the path doesn't exist. |
|
|
468
529
|
| `banner` | — | Path (relative to the project root) to a text file stamped as a license/copyright banner on exported `.js`/`.css`/`.html` files during `kiri export`. May contain the `###DATE###` token, replaced with today's date. Falls back to an auto-generated banner. |
|
|
469
|
-
| *anything else* | — | Free-form key/value pairs (strings, numbers, booleans, lists, nested maps — anything valid YAML). Every key is extracted as a PHP variable (`$author`, `$gtag`, …). Use this for contact info, social links, analytics IDs, SEO keywords, or any project data you want available everywhere. When the top-level `
|
|
470
|
-
|
|
471
|
-
### `
|
|
472
|
-
|
|
473
|
-
Top-level, optional.
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
`@
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
530
|
+
| *anything else* | — | Free-form key/value pairs (strings, numbers, booleans, lists, nested maps — anything valid YAML). Every key is extracted as a PHP variable (`$author`, `$gtag`, …). Use this for contact info, social links, analytics IDs, SEO keywords, or any project data you want available everywhere. When the top-level `seo` block is present, [`LD`](#ld) also reads some of these by convention: `person`, `jobtitle`, `email`, `area`, `knowsabout`, `keywords`, and social-network URL keys (`facebook`, `instagram`, …). |
|
|
531
|
+
|
|
532
|
+
### `seo` block
|
|
533
|
+
|
|
534
|
+
Top-level, optional. The unified SEO surface — replaces the old separate
|
|
535
|
+
`meta:`/`jsonld:` blocks (**breaking in 2.0.0**, see
|
|
536
|
+
[What's new in 2.0.0](#whats-new-in-200)). Its **presence** switches on the
|
|
537
|
+
[`META`](#meta) generator — the standard SEO / social `<meta>` and `<link>`
|
|
538
|
+
tags are then built for every page and injected into its `<head>`. An empty
|
|
539
|
+
`seo: {}` is enough; everything is derived from the `kirigami` block and each
|
|
540
|
+
page's PHPDOC (`@title`, `@description` / `@abstract`, `@keywords`, `@image`,
|
|
541
|
+
`@robots`, `@og_type`, `@canonical`). A tag the layout already hand-writes is
|
|
542
|
+
left untouched. `seo: false` (or `seo: { auto: false }`) keeps the config
|
|
543
|
+
values but stops the injection; no block at all means nothing is injected
|
|
544
|
+
(explicit `META::tag()` / `meta_tag()` calls still emit). Full key reference
|
|
545
|
+
and per-page `@meta_*` tags: [`META` → `seo` config](#meta-config).
|
|
546
|
+
|
|
547
|
+
Nested inside it, `seo.jsonld` is its own **independent** opt-in — a project
|
|
548
|
+
can have META's tags without JSON-LD, or vice versa. Its presence switches on
|
|
549
|
+
the [`LD`](#ld) schema.org JSON-LD generator — an `application/ld+json` graph
|
|
550
|
+
is then injected into every page's `<head>`. An empty `seo: { jsonld: {} }` is
|
|
551
|
+
enough; its keys refine what `LD` otherwise infers from the rest of `seo:`,
|
|
552
|
+
the `kirigami` block, and each page's PHPDOC. `seo: { jsonld: false }` (or
|
|
553
|
+
`{ auto: false }`) keeps the config values but stops the injection. Full key
|
|
554
|
+
reference and per-page `@ld_*` tags: [`LD` → `jsonld` config](#jsonld-config).
|
|
493
555
|
|
|
494
556
|
### `prepros` block
|
|
495
557
|
|
|
@@ -1074,10 +1136,11 @@ than one node — in the `<head>`.
|
|
|
1074
1136
|
|
|
1075
1137
|
#### Automatic mode
|
|
1076
1138
|
|
|
1077
|
-
Opt in by adding a
|
|
1078
|
-
|
|
1079
|
-
`
|
|
1080
|
-
the `kirigami` block, and
|
|
1139
|
+
Opt in by adding a `jsonld` sub-block under `seo:` in `kirigami.yaml` (nested
|
|
1140
|
+
inside the same block [`META`](#meta) reads, independent of the rest of it) —
|
|
1141
|
+
an empty `seo: { jsonld: {} }` is enough. A `post_render` hook then injects a
|
|
1142
|
+
graph built from that sub-block, the loose keys of the `kirigami` block, and
|
|
1143
|
+
the current page's PHPDOC:
|
|
1081
1144
|
|
|
1082
1145
|
- an `Organization` node (`@id` `#organization`) — `name`/`url`/`description`
|
|
1083
1146
|
from `project`/`baseurl`/`description`, `sameAs` gathered from every
|
|
@@ -1092,13 +1155,14 @@ the `kirigami` block, and the current page's PHPDOC:
|
|
|
1092
1155
|
- a `WebPage` node for the page — see the per-page tags below;
|
|
1093
1156
|
- a `BreadcrumbList` for every non-home page, derived from the `_index.php`
|
|
1094
1157
|
ancestor trail (home → each parent section → this page). No `@breadcrumb`
|
|
1095
|
-
opt-in needed — it is always attempted while the `jsonld
|
|
1158
|
+
opt-in needed — it is always attempted while the `jsonld` sub-block is on.
|
|
1096
1159
|
Disable it for one page with `@ld_breadcrumb false`.
|
|
1097
1160
|
|
|
1098
|
-
Remove the `jsonld
|
|
1099
|
-
to stop the automatic pass
|
|
1100
|
-
|
|
1101
|
-
|
|
1161
|
+
Remove the `jsonld` sub-block (or set `seo: { jsonld: false }` /
|
|
1162
|
+
`{ jsonld: { auto: false } }`) to stop the automatic pass — the rest of `seo:`
|
|
1163
|
+
(META's tags) keeps working either way. A page whose rendered `<head>` already
|
|
1164
|
+
contains an `application/ld+json` script is never touched, so hand-rolled
|
|
1165
|
+
markup keeps working.
|
|
1102
1166
|
|
|
1103
1167
|
**Per-page PHPDOC tags** — these feed the page node (and override the generic
|
|
1104
1168
|
`@title` / `@description` / `@datePublished` fallbacks):
|
|
@@ -1126,9 +1190,9 @@ working.
|
|
|
1126
1190
|
#### Explicit builders
|
|
1127
1191
|
|
|
1128
1192
|
Call these from a page template or from a `prepros.includes` file. Nodes added
|
|
1129
|
-
this way are always emitted — with or without a `jsonld
|
|
1130
|
-
graph the automatic pass uses, so the two combine; a node with a stable
|
|
1131
|
-
merged on repeat calls.
|
|
1193
|
+
this way are always emitted — with or without a `jsonld` sub-block — and share
|
|
1194
|
+
the graph the automatic pass uses, so the two combine; a node with a stable
|
|
1195
|
+
`@id` is merged on repeat calls.
|
|
1132
1196
|
|
|
1133
1197
|
```php
|
|
1134
1198
|
LD::add(string|array $type, array $props = [], ?string $id = null): array // build + register a node
|
|
@@ -1181,10 +1245,12 @@ Same API from procedural code: `ld_add()`, `ld_node()`, `ld_ref()`,
|
|
|
1181
1245
|
|
|
1182
1246
|
#### `jsonld` config
|
|
1183
1247
|
|
|
1184
|
-
`jsonld
|
|
1185
|
-
`
|
|
1186
|
-
|
|
1187
|
-
|
|
1248
|
+
`jsonld` is a sub-block of the **top-level** `seo:` block of `kirigami.yaml`
|
|
1249
|
+
(nested alongside [`META`](#meta)'s own keys), and its presence is what
|
|
1250
|
+
**switches automatic injection on** — independently of the rest of `seo:`. An
|
|
1251
|
+
empty `seo: { jsonld: {} }` is enough — everything is then derived from the
|
|
1252
|
+
`kirigami` block's loose keys. Adding keys overrides those inferences; all are
|
|
1253
|
+
optional.
|
|
1188
1254
|
|
|
1189
1255
|
```yaml
|
|
1190
1256
|
kirigami:
|
|
@@ -1194,20 +1260,21 @@ kirigami:
|
|
|
1194
1260
|
jobtitle: Anthropologue
|
|
1195
1261
|
facebook: https://www.facebook.com/humainhumainconsultation.ethnographie/
|
|
1196
1262
|
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1263
|
+
seo:
|
|
1264
|
+
jsonld: # nested; the sub-block being present
|
|
1265
|
+
type: ProfessionalService # is the switch — `{}` also works
|
|
1266
|
+
lang: fr-CA # inLanguage on WebSite / WebPage (default: en)
|
|
1267
|
+
logo: assets/logo.png # absolute, or relative to baseurl
|
|
1268
|
+
knowsAbout: [Ethnographie, Recherche qualitative]
|
|
1269
|
+
address:
|
|
1270
|
+
addressLocality: Québec
|
|
1271
|
+
addressCountry: CA
|
|
1272
|
+
search: https://humainhumain.com/?q={search_term_string}
|
|
1206
1273
|
```
|
|
1207
1274
|
|
|
1208
1275
|
| Key | Type | Description |
|
|
1209
1276
|
|-----|------|-------------|
|
|
1210
|
-
| `auto` | `bool` | Inject the `<script>` automatically. Default `true` **once the `jsonld
|
|
1277
|
+
| `auto` | `bool` | Inject the `<script>` automatically. Default `true` **once the `jsonld` sub-block exists**. Set `auto: false` (or `seo: { jsonld: false }`) to keep the sub-block for its config values but stop the automatic injection — `LD::script()` / `ld_script()` can still place it by hand. |
|
|
1211
1278
|
| `type` | `string` | `@type` for the main entity — `Organization`, `ProfessionalService`, `LocalBusiness`, … |
|
|
1212
1279
|
| `name` / `url` / `description` | `string` | Main-entity / WebSite fields. Default to `project` / `baseurl` / `description`. |
|
|
1213
1280
|
| `logo` / `image` | `string` | Absolute URL or path relative to `baseurl`. `image` defaults to `logo`. |
|
|
@@ -1230,12 +1297,13 @@ tags a browser and a link-preview crawler read: `<title>`, `<meta name="…">`,
|
|
|
1230
1297
|
`<meta property="og:…">`, `<meta name="twitter:…">`, and a handful of `<link>`s.
|
|
1231
1298
|
|
|
1232
1299
|
It draws on the same sources, in this order of precedence: the page's PHPDOC, the
|
|
1233
|
-
top-level `
|
|
1234
|
-
Every tag is emitted **only when it can be resolved** — no value, no
|
|
1235
|
-
tag the page's layout already writes by hand is detected and
|
|
1236
|
-
in beside an existing `header.php` without duplicating
|
|
1300
|
+
top-level `seo:` block, then the loose `kirigami:` keys and the `seo.jsonld`
|
|
1301
|
+
sub-block. Every tag is emitted **only when it can be resolved** — no value, no
|
|
1302
|
+
tag — and a tag the page's layout already writes by hand is detected and
|
|
1303
|
+
skipped, so it drops in beside an existing `header.php` without duplicating
|
|
1304
|
+
anything.
|
|
1237
1305
|
|
|
1238
|
-
**Automatic mode** is opt-in: the top-level `
|
|
1306
|
+
**Automatic mode** is opt-in: the top-level `seo:` block (empty `seo: {}` is
|
|
1239
1307
|
enough) turns on injection into every page's `<head>`, right before `</head>`.
|
|
1240
1308
|
|
|
1241
1309
|
```yaml
|
|
@@ -1247,10 +1315,10 @@ kirigami:
|
|
|
1247
1315
|
keywords: [ethnographie, consultation publique, sciences sociales]
|
|
1248
1316
|
author: Maxime Larrivée-Roy
|
|
1249
1317
|
|
|
1250
|
-
|
|
1251
|
-
meta: # top-level; the block being present is the switch
|
|
1318
|
+
seo: # top-level; the block being present is the switch
|
|
1252
1319
|
twitter: "@humainhumain"
|
|
1253
1320
|
themeColor: "#0b7285"
|
|
1321
|
+
jsonld: {} # independent opt-in — META reads its logo / image / lang / person too
|
|
1254
1322
|
```
|
|
1255
1323
|
|
|
1256
1324
|
Per-page, from the PHPDOC block — each falls back to the generic page tag:
|
|
@@ -1260,13 +1328,13 @@ Per-page, from the PHPDOC block — each falls back to the generic page tag:
|
|
|
1260
1328
|
| `@meta false` | skip metadata for this page entirely | — (`@meta_ignore true` also works) |
|
|
1261
1329
|
| `@meta_title` | `<title>`, `og:title`, `twitter:title` | `@title` |
|
|
1262
1330
|
| `@meta_description` | `description`, `og:description`, `twitter:description` | `@description` / `@abstract` / `@excerpt` / `@summary`, then the site `description` |
|
|
1263
|
-
| `@meta_keywords` | `<meta name="keywords">` | `@keywords`, then `
|
|
1264
|
-
| `@meta_image` | `og:image`, `twitter:image` | `@image` / `@ogimage`, then `
|
|
1265
|
-
| `@meta_robots` | `<meta name="robots">` | `@robots`, then `
|
|
1266
|
-
| `@meta_type` | `og:type` | `@og_type`, then `
|
|
1331
|
+
| `@meta_keywords` | `<meta name="keywords">` | `@keywords`, then `seo.keywords` |
|
|
1332
|
+
| `@meta_image` | `og:image`, `twitter:image` | `@image` / `@ogimage`, then `seo.image` |
|
|
1333
|
+
| `@meta_robots` | `<meta name="robots">` | `@robots`, then `seo.robots` |
|
|
1334
|
+
| `@meta_type` | `og:type` | `@og_type`, then `seo.ogType` |
|
|
1267
1335
|
| `@canonical` | `<link rel="canonical">` | derived from the file path + `baseurl` |
|
|
1268
1336
|
|
|
1269
|
-
**Manual builders** — always emitted (with or without a `
|
|
1337
|
+
**Manual builders** — always emitted (with or without a `seo:` block), still
|
|
1270
1338
|
de-duplicated against the page:
|
|
1271
1339
|
|
|
1272
1340
|
```php
|
|
@@ -1288,20 +1356,22 @@ Same API from procedural code: `meta_tag()`, `meta_link()`, `meta_raw()`,
|
|
|
1288
1356
|
|
|
1289
1357
|
#### `meta` config
|
|
1290
1358
|
|
|
1291
|
-
|
|
1292
|
-
`
|
|
1359
|
+
These keys live directly under the **top-level** `seo:` block of
|
|
1360
|
+
`kirigami.yaml` (a sibling of `kirigami:`, `prepros:`, …) — `jsonld` is the one
|
|
1361
|
+
sub-block among them, documented separately in [`LD` → `jsonld` config](#jsonld-config).
|
|
1362
|
+
All keys are optional.
|
|
1293
1363
|
|
|
1294
1364
|
| Key | Type | Description |
|
|
1295
1365
|
|-----|------|-------------|
|
|
1296
|
-
| `auto` | `bool` | Inject the tags automatically. Default `true` **once the `
|
|
1366
|
+
| `auto` | `bool` | Inject the tags automatically. Default `true` **once the `seo:` block exists**. `auto: false` (or `seo: false`) keeps the block for its values but stops the injection — `META::tags()` / `meta_tags()` can place them by hand. Independent of `jsonld.auto`. |
|
|
1297
1367
|
| `titleFormat` | `string` | `<title>` template for a normal page. Tokens `{title}`, `{project}`, `{tagline}`. Dangling separators from an empty token are trimmed. Default `{title} — {project}`. |
|
|
1298
1368
|
| `titleFormatHome` | `string` | Title template when the page has no `@title` (home / section landings). Default `{project} — {tagline}`. |
|
|
1299
|
-
| `description` | `string` | Default description for pages with no `@description` / `@abstract`. Defaults to
|
|
1300
|
-
| `keywords` | `string[]` \| `string` | Default `keywords` content (list or comma string). Defaults to
|
|
1369
|
+
| `description` | `string` | Default description for pages with no `@description` / `@abstract`. Defaults to `jsonld.description` / loose `description`. |
|
|
1370
|
+
| `keywords` | `string[]` \| `string` | Default `keywords` content (list or comma string). Defaults to `jsonld.keywords` / loose `keywords`. |
|
|
1301
1371
|
| `robots` | `string` | Default robots directive. Default `index, follow`. `robots: false` omits the tag. |
|
|
1302
1372
|
| `language` | `string` | BCP-47 tag → `<meta name="language">` and, dash→underscore, `og:locale`. Defaults to `jsonld.lang` / loose `lang` / `language`, then `en`. |
|
|
1303
1373
|
| `generator` | `string` \| `false` | `<meta name="generator">`. Default `Kirigami`; `false` omits it. |
|
|
1304
|
-
| `author` / `designer` | `string` | Default to the loose `author` / `designer` keys (author also falls back to
|
|
1374
|
+
| `author` / `designer` | `string` | Default to the loose `author` / `designer` keys (author also falls back to `jsonld.person`'s name). `designer` is not emitted unless set. |
|
|
1305
1375
|
| `themeColor` | `string` | `<meta name="theme-color">`. Not emitted unless set. |
|
|
1306
1376
|
| `image` | `string` | Default `og:image` / `twitter:image` — absolute URL or path relative to `baseurl`. Defaults to `jsonld.image` → `jsonld.logo` → loose `image` / `ogimage`. |
|
|
1307
1377
|
| `ogType` | `string` | Default `og:type`. Default `website`. |
|
|
@@ -1309,6 +1379,7 @@ Same API from procedural code: `meta_tag()`, `meta_link()`, `meta_raw()`,
|
|
|
1309
1379
|
| `twitter` | `string` \| `map` | Handle for `twitter:site` / `twitter:creator`. A bare string (with/without `@`, or a profile URL) fills both; a map takes `site` / `creator` separately. |
|
|
1310
1380
|
| `canonical` | `bool` | Emit `<link rel="canonical">`. Default `true`. |
|
|
1311
1381
|
| `favicon` / `appleTouchIcon` / `humans` | `string` \| `bool` | `<link rel="icon">` / `rel="apple-touch-icon"` / `rel="author"`. A path sets it (page-relative when a bare filename); `true` forces the default file (`favicon.ico` / `apple-touch-icon.png` / `humans.txt`); omitted, the default file is auto-detected on disk at the source root; `false` disables it. |
|
|
1382
|
+
| `jsonld` | `object` \| `bool` | Sub-block for `LD`'s schema.org JSON-LD — its own independent opt-in. See [`LD` → `jsonld` config](#jsonld-config). |
|
|
1312
1383
|
|
|
1313
1384
|
---
|
|
1314
1385
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kirigami/php-prepros",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "PHP preprocessor for the Kirigami static site generator. Compile PHP page templates to clean, deployable HTML — with zero server dependency.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"kirigami",
|
|
@@ -45,14 +45,14 @@
|
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@kirigami/php-wasm": "8.5.10-5",
|
|
48
|
-
"@kirigami/struct-walker": "1.0.
|
|
48
|
+
"@kirigami/struct-walker": "1.0.5",
|
|
49
49
|
"picomatch": "^4.0.7"
|
|
50
50
|
},
|
|
51
51
|
"repository": {
|
|
52
52
|
"type": "git",
|
|
53
53
|
"url": "git+https://github.com/php-kirigami/kirigami.git"
|
|
54
54
|
},
|
|
55
|
-
"homepage": "https://
|
|
55
|
+
"homepage": "https://php-kirigami.github.io",
|
|
56
56
|
"bugs": {
|
|
57
57
|
"url": "https://github.com/php-kirigami/kirigami/issues"
|
|
58
58
|
}
|
|
@@ -7,21 +7,23 @@ declare(strict_types=1);
|
|
|
7
7
|
*
|
|
8
8
|
* Collects schema.org nodes during a render and emits them as a single
|
|
9
9
|
* `<script type="application/ld+json">` block in the page `<head>`, built from
|
|
10
|
-
* the
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* the `seo.jsonld` sub-block of `kirigami.yaml` — nested under the same `seo:`
|
|
11
|
+
* block `META` reads, one on/off switch for the whole SEO surface — and the
|
|
12
|
+
* loose keys of the `kirigami:` block (`person`, `jobtitle`, `area`,
|
|
13
|
+
* `knowsabout`, `keywords`, `facebook`, … that Kirigami projects already use).
|
|
13
14
|
*
|
|
14
15
|
* Two ways to use it, and they combine:
|
|
15
16
|
*
|
|
16
|
-
* 1. Automatic — opt-in. As soon as `kirigami.yaml` carries a
|
|
17
|
-
* `jsonld
|
|
18
|
-
* injects an `Organization` (+ `Person`, `WebSite`,
|
|
19
|
-
* `BreadcrumbList` built from the `_index.php` ancestor
|
|
20
|
-
* derived from the block, the loose keys and the current
|
|
21
|
-
* Without a `jsonld
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
17
|
+
* 1. Automatic — opt-in. As soon as `kirigami.yaml`'s `seo:` block carries a
|
|
18
|
+
* `jsonld` sub-block (even an empty one, `seo: { jsonld: {} }`), a
|
|
19
|
+
* `post_render` hook injects an `Organization` (+ `Person`, `WebSite`,
|
|
20
|
+
* `WebPage`, and a `BreadcrumbList` built from the `_index.php` ancestor
|
|
21
|
+
* trail) `@graph` derived from the block, the loose keys and the current
|
|
22
|
+
* page's PHPDOC. Without a `jsonld` sub-block nothing is injected — the
|
|
23
|
+
* rest of `seo:` (META's own concerns) works independently of it. Turn it
|
|
24
|
+
* back off with `seo: { jsonld: false }` (or `{ auto: false }`), or per
|
|
25
|
+
* page with `@ld false` in the template's PHPDOC. A page that already
|
|
26
|
+
* hand-writes an `application/ld+json` script is left untouched.
|
|
25
27
|
*
|
|
26
28
|
* Per-page PHPDOC tags feed the page node:
|
|
27
29
|
* @ld false skip JSON-LD for this page (or @ld_ignore true)
|
|
@@ -34,7 +36,7 @@ declare(strict_types=1);
|
|
|
34
36
|
* @ld_breadcrumb false no BreadcrumbList for this page
|
|
35
37
|
*
|
|
36
38
|
* 2. Explicit. Call the builders from a template or from an `includes` file.
|
|
37
|
-
* Nodes added this way are always emitted, `jsonld
|
|
39
|
+
* Nodes added this way are always emitted, `seo.jsonld` sub-block or not:
|
|
38
40
|
*
|
|
39
41
|
* LD::add('Recipe', [ 'name' => 'Tarte', 'recipeYield' => '6' ]);
|
|
40
42
|
* LD::article([ 'headline' => $title, 'author' => LD::ref('#person') ]);
|
|
@@ -76,8 +78,8 @@ final class LD
|
|
|
76
78
|
// -----------------------------------------------------------------------
|
|
77
79
|
|
|
78
80
|
/**
|
|
79
|
-
* Resolved JSON-LD configuration, merging the `jsonld
|
|
80
|
-
* loose top-level keys of the `kirigami:` block.
|
|
81
|
+
* Resolved JSON-LD configuration, merging the `seo.jsonld` sub-block with
|
|
82
|
+
* the loose top-level keys of the `kirigami:` block.
|
|
81
83
|
*/
|
|
82
84
|
public static function config(): object
|
|
83
85
|
{
|
|
@@ -87,7 +89,7 @@ final class LD
|
|
|
87
89
|
$raw = self::jsonldRaw();
|
|
88
90
|
$j = is_object($raw) ? $raw : new stdClass;
|
|
89
91
|
|
|
90
|
-
// Automatic injection is opt-in: it needs a
|
|
92
|
+
// Automatic injection is opt-in: it needs a `seo.jsonld` sub-block (an
|
|
91
93
|
// empty map counts). `jsonld: false` / `jsonld: { auto: false }` turn it off.
|
|
92
94
|
$enabled = is_object($raw) || $raw === true;
|
|
93
95
|
if ($enabled && isset($j->auto) && !self::truthy($j->auto)) $enabled = false;
|
|
@@ -371,8 +373,8 @@ final class LD
|
|
|
371
373
|
|
|
372
374
|
/**
|
|
373
375
|
* A `BreadcrumbList`. With no argument it is derived from the page's
|
|
374
|
-
* ancestor `_index.php` trail (always attempted while the `jsonld
|
|
375
|
-
* is on — no `@breadcrumb` opt-in needed; disable per page with
|
|
376
|
+
* ancestor `_index.php` trail (always attempted while the `seo.jsonld`
|
|
377
|
+
* sub-block is on — no `@breadcrumb` opt-in needed; disable per page with
|
|
376
378
|
* `@ld_breadcrumb false`). Pass `$items` as `[['name' => …, 'url' => …], …]`
|
|
377
379
|
* to build it by hand.
|
|
378
380
|
*/
|
|
@@ -544,7 +546,7 @@ final class LD
|
|
|
544
546
|
if (self::pageOptedOut()) return $html;
|
|
545
547
|
if (stripos($html, 'application/ld+json') !== false) return $html;
|
|
546
548
|
|
|
547
|
-
// script() autofills the defaults only when the `jsonld
|
|
549
|
+
// script() autofills the defaults only when the `seo.jsonld` sub-block
|
|
548
550
|
// opted in; otherwise it emits nothing unless a template added
|
|
549
551
|
// nodes by hand, in which case those are still injected.
|
|
550
552
|
$script = self::script();
|
|
@@ -583,10 +585,11 @@ final class LD
|
|
|
583
585
|
return new stdClass;
|
|
584
586
|
}
|
|
585
587
|
|
|
586
|
-
/** The raw
|
|
588
|
+
/** The raw `seo.jsonld` sub-block: an object, `false`, `true`, or `null` when absent. */
|
|
587
589
|
private static function jsonldRaw(): mixed
|
|
588
590
|
{
|
|
589
|
-
|
|
591
|
+
$seo = (isset(PREPROS::$config) && is_object(PREPROS::$config)) ? (PREPROS::$config->seo ?? null) : null;
|
|
592
|
+
return is_object($seo) ? ($seo->jsonld ?? null) : null;
|
|
590
593
|
}
|
|
591
594
|
|
|
592
595
|
private static function pageInfo(): object
|
|
@@ -10,9 +10,9 @@ declare(strict_types=1);
|
|
|
10
10
|
* precedence:
|
|
11
11
|
*
|
|
12
12
|
* 1. the page's own PHPDOC block (`@title`, `@description`, `@image`, …),
|
|
13
|
-
* 2. the top-level `
|
|
14
|
-
* 3. the loose keys of the `kirigami:` block and the
|
|
15
|
-
*
|
|
13
|
+
* 2. the top-level `seo:` block of `kirigami.yaml` (config + overrides),
|
|
14
|
+
* 3. the loose keys of the `kirigami:` block and the `seo.jsonld` sub-block
|
|
15
|
+
* (`description`, `keywords`, `author`, `person`, `lang`, `logo`,
|
|
16
16
|
* `image`, …) — the same values `LD` already reads.
|
|
17
17
|
*
|
|
18
18
|
* It only ever emits what it can resolve: a tag with no value is skipped, and a
|
|
@@ -20,11 +20,13 @@ declare(strict_types=1);
|
|
|
20
20
|
* `<link rel="canonical">`, …) is left untouched — so it slots in next to an
|
|
21
21
|
* existing `header.php` without doubling anything up.
|
|
22
22
|
*
|
|
23
|
-
* Automatic injection is **opt-in**: it needs a top-level `
|
|
24
|
-
* empty map, `
|
|
23
|
+
* Automatic injection is **opt-in**: it needs a top-level `seo:` block (an
|
|
24
|
+
* empty map, `seo: {}`, is enough). `seo: false` (or `seo: { auto: false }`)
|
|
25
25
|
* keeps the config values but stops the injection; no block at all means
|
|
26
26
|
* nothing is injected — an explicit `META::tag()` call from a template still
|
|
27
|
-
* emits.
|
|
27
|
+
* emits. `LD`'s schema.org JSON-LD lives right alongside it, under `seo.jsonld`
|
|
28
|
+
* — one block, one on/off switch for the whole SEO surface, `jsonld` toggled
|
|
29
|
+
* independently within it (see `LD`'s own docblock).
|
|
28
30
|
*
|
|
29
31
|
* Per-page PHPDOC tags, each falling back to the generic page tag:
|
|
30
32
|
*
|
|
@@ -37,7 +39,7 @@ declare(strict_types=1);
|
|
|
37
39
|
* @meta_type <type> og:type (default @og_type)
|
|
38
40
|
* @canonical <url> <link rel="canonical"> (default: derived from the file path + baseurl)
|
|
39
41
|
*
|
|
40
|
-
* Manual use from a template or an `includes` file — always emitted, `
|
|
42
|
+
* Manual use from a template or an `includes` file — always emitted, `seo:`
|
|
41
43
|
* block or not, and still de-duplicated against the page:
|
|
42
44
|
*
|
|
43
45
|
* META::tag('twitter:image', 'https://…/card.png'); // name= or property= picked from the key
|
|
@@ -64,8 +66,8 @@ final class META
|
|
|
64
66
|
// -----------------------------------------------------------------------
|
|
65
67
|
|
|
66
68
|
/**
|
|
67
|
-
* Resolved metadata configuration, merging the `
|
|
68
|
-
* keys of the `kirigami:` block and the `jsonld
|
|
69
|
+
* Resolved metadata configuration, merging the `seo:` block with the loose
|
|
70
|
+
* keys of the `kirigami:` block and the `seo.jsonld` sub-block.
|
|
69
71
|
*/
|
|
70
72
|
public static function config(): object
|
|
71
73
|
{
|
|
@@ -76,7 +78,7 @@ final class META
|
|
|
76
78
|
$m = is_object($raw) ? $raw : new stdClass;
|
|
77
79
|
$ld = self::jsonld();
|
|
78
80
|
|
|
79
|
-
// Opt-in: needs a top-level `
|
|
81
|
+
// Opt-in: needs a top-level `seo:` block (an empty map counts).
|
|
80
82
|
$enabled = is_object($raw) || $raw === true;
|
|
81
83
|
if ($enabled && isset($m->auto) && !self::truthy($m->auto)) $enabled = false;
|
|
82
84
|
|
|
@@ -188,7 +190,7 @@ final class META
|
|
|
188
190
|
/** @var array<int,array{0:string,1:string}> [probe regex, tag html] */
|
|
189
191
|
$lines = [];
|
|
190
192
|
|
|
191
|
-
// The auto block only builds when the `
|
|
193
|
+
// The auto block only builds when the `seo:` block opted in; an
|
|
192
194
|
// explicit META::tag() call still lands through self::$extra below.
|
|
193
195
|
if ($c->enabled) {
|
|
194
196
|
$relroot = self::relroot();
|
|
@@ -366,16 +368,17 @@ final class META
|
|
|
366
368
|
return new stdClass;
|
|
367
369
|
}
|
|
368
370
|
|
|
369
|
-
/** The raw top-level `
|
|
371
|
+
/** The raw top-level `seo:` block: object, `false`, `true`, or `null`. */
|
|
370
372
|
private static function metaRaw(): mixed
|
|
371
373
|
{
|
|
372
|
-
return (isset(PREPROS::$config) && is_object(PREPROS::$config)) ? (PREPROS::$config->
|
|
374
|
+
return (isset(PREPROS::$config) && is_object(PREPROS::$config)) ? (PREPROS::$config->seo ?? null) : null;
|
|
373
375
|
}
|
|
374
376
|
|
|
375
|
-
/** The
|
|
377
|
+
/** The `seo.jsonld` sub-block as an object (empty when absent / disabled). */
|
|
376
378
|
private static function jsonld(): object
|
|
377
379
|
{
|
|
378
|
-
$
|
|
380
|
+
$seo = self::metaRaw();
|
|
381
|
+
$raw = is_object($seo) ? ($seo->jsonld ?? null) : null;
|
|
379
382
|
return is_object($raw) ? $raw : new stdClass;
|
|
380
383
|
}
|
|
381
384
|
|
package/src/prepros.js
CHANGED
|
@@ -46,8 +46,9 @@ const getPHPInstance = async () => {
|
|
|
46
46
|
preprosConfig.timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
47
47
|
preprosConfig.root = joinWith('/project/', config?.kirigami?.root);
|
|
48
48
|
preprosConfig.data = config.kirigami || {};
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
// The unified SEO block — META reads it directly; LD reads its own
|
|
50
|
+
// `jsonld` sub-key (see META/LD's docblocks). One block, one toggle.
|
|
51
|
+
preprosConfig.seo = config.seo ?? null;
|
|
51
52
|
// META auto-detects favicon / apple-touch-icon / humans.txt at the
|
|
52
53
|
// source root; those extensions aren't mounted into the sandbox, so the
|
|
53
54
|
// presence check is done here on the real filesystem instead.
|