@kirigami/php-prepros 1.2.0 → 1.6.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 +491 -12
- package/index.d.ts +101 -31
- package/index.js +1 -1
- package/package.json +1 -1
- package/src/imagebatch.php +67 -0
- package/src/libraries/aliases.inc.php +836 -0
- package/src/libraries/curl.class.php +15 -3
- package/src/libraries/fs.class.php +161 -6
- package/src/libraries/html.class.php +69 -0
- package/src/libraries/img.class.php +19 -4
- package/src/libraries/ld.class.php +804 -0
- package/src/libraries/md.class.php +26 -3
- package/src/libraries/md.plugins.php +7 -1
- package/src/libraries/prepros.class.php +114 -4
- package/src/libraries/prepros.plugins.php +32 -6
- package/src/libraries/std.class.php +3 -0
- package/src/libraries/str.class.php +1 -1
- package/src/prepros.js +77 -5
- package/src/prepros.php +9 -2
- package/src/runenv.php +5 -2
- package/src/utils.inc.php +11 -1
package/README.md
CHANGED
|
@@ -34,11 +34,16 @@ Part of the **Kirigami** project ecosystem.
|
|
|
34
34
|
- [@kirigami/php-prepros](#kirigamiphp-prepros)
|
|
35
35
|
- [Overview](#overview)
|
|
36
36
|
- [Table of contents](#table-of-contents)
|
|
37
|
+
- [What's new in 1.6.0](#whats-new-in-160)
|
|
38
|
+
- [What's new in 1.4.0](#whats-new-in-140)
|
|
39
|
+
- [What's new in 1.3.0](#whats-new-in-130)
|
|
40
|
+
- [What's new in 1.2.1](#whats-new-in-121)
|
|
37
41
|
- [What's new in 1.2.0](#whats-new-in-120)
|
|
38
42
|
- [How it works](#how-it-works)
|
|
39
43
|
- [Installation](#installation)
|
|
40
44
|
- [Configuration — `kirigami.yaml`](#configuration--kirigamiyaml)
|
|
41
45
|
- [`kirigami` block](#kirigami-block)
|
|
46
|
+
- [`jsonld` block](#jsonld-block)
|
|
42
47
|
- [`prepros` block](#prepros-block)
|
|
43
48
|
- [`image` block](#image-block)
|
|
44
49
|
- [`plugins` block](#plugins-block)
|
|
@@ -50,11 +55,13 @@ Part of the **Kirigami** project ecosystem.
|
|
|
50
55
|
- [PHPDOC header](#phpdoc-header)
|
|
51
56
|
- [Auto-loading data files](#auto-loading-data-files)
|
|
52
57
|
- [`@content` and `@indent`](#content-and-indent)
|
|
58
|
+
- [Built-in tags](#built-in-tags)
|
|
53
59
|
- [JavaScript API](#javascript-api)
|
|
54
60
|
- [`render(file?)`](#renderfile)
|
|
55
61
|
- [`sitemap()`](#sitemap)
|
|
56
62
|
- [`runenv(script, paths?, ...args)`](#runenvscript-paths-args)
|
|
57
63
|
- [`mountPath(localPath, virtualDir?, php?)`](#mountpathlocalpath-virtualdir-php)
|
|
64
|
+
- [`processImages(jobs)`](#processimagesjobs)
|
|
58
65
|
- [PHP classes reference](#php-classes-reference)
|
|
59
66
|
- [PREPROS](#prepros)
|
|
60
67
|
- [`PREPROS::render(string $file)`](#preprosrenderstring-file)
|
|
@@ -66,6 +73,10 @@ Part of the **Kirigami** project ecosystem.
|
|
|
66
73
|
- [HTML](#html)
|
|
67
74
|
- [YAML](#yaml)
|
|
68
75
|
- [SCHEMA](#schema)
|
|
76
|
+
- [LD](#ld)
|
|
77
|
+
- [Automatic mode](#automatic-mode)
|
|
78
|
+
- [Explicit builders](#explicit-builders)
|
|
79
|
+
- [`jsonld` config](#jsonld-config)
|
|
69
80
|
- [CACHE](#cache)
|
|
70
81
|
- [IMG](#img)
|
|
71
82
|
- [FS](#fs)
|
|
@@ -76,6 +87,7 @@ Part of the **Kirigami** project ecosystem.
|
|
|
76
87
|
- [OBF](#obf)
|
|
77
88
|
- [STD](#std)
|
|
78
89
|
- [Bundled polyfills](#bundled-polyfills)
|
|
90
|
+
- [Procedural shortcuts (aliases)](#procedural-shortcuts-aliases)
|
|
79
91
|
- [Plugin system](#plugin-system)
|
|
80
92
|
- [PREPROS tags](#prepros-tags)
|
|
81
93
|
- [PREPROS hooks](#prepros-hooks)
|
|
@@ -91,6 +103,110 @@ Part of the **Kirigami** project ecosystem.
|
|
|
91
103
|
|
|
92
104
|
---
|
|
93
105
|
|
|
106
|
+
## What's new in 1.6.0
|
|
107
|
+
|
|
108
|
+
- **Managed `<head>` (`prepros.head`).** Every rendered page's `<head>` is now
|
|
109
|
+
auto-wired: a tiny theme/FOUC guard as the first child (adds the `js` class,
|
|
110
|
+
applies the stored `data-theme` before first paint), a
|
|
111
|
+
`<link rel="stylesheet">` for every `sass` task output, and a `<script>` (no
|
|
112
|
+
`defer`, just before `</body>`) for every `esbuild` task output — each with a
|
|
113
|
+
per-page relative path and a `?<timestamp>` cache-bust. A file already
|
|
114
|
+
referenced in the page is left alone, so you can still hand-place one. Turn it
|
|
115
|
+
off with `prepros: { head: false }`, or `head: false` on a single sass/esbuild
|
|
116
|
+
task. A template's `header.php` no longer wires assets at all.
|
|
117
|
+
|
|
118
|
+
- **`HTML::format()` indents `<pre><code>`.** A fenced code block's lines are
|
|
119
|
+
shifted to the block's nesting depth so the HTML source stays readable
|
|
120
|
+
(relative indentation preserved). The exact leading run is stripped again
|
|
121
|
+
before it's shown — at build time by `@kirigami/plugin-highlight`, otherwise
|
|
122
|
+
by a ~250-byte de-indent script `prepros.head` injects before `</body>` (only
|
|
123
|
+
when `format` is on; it skips blocks a highlighter already flattened). A bare
|
|
124
|
+
`<pre>` and `<textarea>` are still emitted byte-for-byte.
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## What's new in 1.4.0
|
|
129
|
+
|
|
130
|
+
A round of fixes to the rough edges that showed up building a full site from
|
|
131
|
+
scratch — mostly developer-experience, all backward compatible.
|
|
132
|
+
|
|
133
|
+
- **`HTML::format()` keeps `<pre>` / `<textarea>` verbatim.** Their line breaks,
|
|
134
|
+
indentation and blank lines are no longer collapsed, so a fenced code block
|
|
135
|
+
survives the formatter intact — `format: true` and Markdown code blocks now
|
|
136
|
+
coexist. (1.6.0 refines this: a `<pre><code>` block is re-indented to its
|
|
137
|
+
nesting depth and de-indented again before display.)
|
|
138
|
+
- **The default Markdown plugins load out of the box.** `{% callout %}`,
|
|
139
|
+
`{% youtube %}`, `{% codepen %}` and `{% checklist %}` are registered
|
|
140
|
+
automatically (`md.plugins.php` is auto-included from `MD`), as the docs always
|
|
141
|
+
said. Drop one with `MD::unregisterPlugin('name')` or shadow it with your own
|
|
142
|
+
`MD::registerPlugin()`.
|
|
143
|
+
- **Build errors you can actually read.** A fatal in a template (a bad call, a
|
|
144
|
+
`null` argument, a `TypeError`…) comes back as a structured failure with the
|
|
145
|
+
message, the offending page and the `file:line` — never a bare
|
|
146
|
+
`Error: undefined`. The `try/catch` now covers `Throwable`, not just
|
|
147
|
+
`Exception`. PHP warnings and notices no longer sink an otherwise-clean build:
|
|
148
|
+
they surface as `warnings` on the result. `kiri` prints the message, the page,
|
|
149
|
+
and the tail of the PHP stderr/debug output on failure.
|
|
150
|
+
- **PHPDOC parsing.** A tag value may now wrap onto the following *indented*
|
|
151
|
+
continuation lines instead of being silently truncated at the first line. And
|
|
152
|
+
an `@word` written in the block's prose is ignored rather than overwriting a
|
|
153
|
+
real tag — only lines that *start* with `@` open a tag.
|
|
154
|
+
- **`FS::getBreadcrumb()` / `FS::getChildren()`** are anchored on the page being
|
|
155
|
+
rendered (`PREPROS::$file`), so they return the right trail / child list when
|
|
156
|
+
called from a layout include, a partial, or a helper function — not only
|
|
157
|
+
straight from the template. Pass an explicit path to override.
|
|
158
|
+
- **`{% tag %}` inside a code span or code block stays literal** (`` `{% badge %}` ``
|
|
159
|
+
renders as text) instead of being expanded — or leaking an unrestored
|
|
160
|
+
placeholder.
|
|
161
|
+
- **`IMG` never upscales.** A requested size larger than the source is clamped
|
|
162
|
+
down to the source instead of throwing an opaque encoder error (the AVIF
|
|
163
|
+
encoder in particular).
|
|
164
|
+
- **Build-time tokens expand at render time.** `###YEAR###`, `###TIMESTAMP###`
|
|
165
|
+
and `###TODAY###` are substituted when each page is generated, so
|
|
166
|
+
`kiri build` / `kiri watch` previews show real values, not the literal token
|
|
167
|
+
(previously only `kiri export` replaced them).
|
|
168
|
+
- **`page_info` hook robustness.** The first built-in callback accepts either the
|
|
169
|
+
`[$file, $info]` pair the hook fires with or the bare `$info` object a later
|
|
170
|
+
callback receives, so a custom `page_info` hook can't fatal on the argument
|
|
171
|
+
shape. See [PREPROS hooks](#prepros-hooks).
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## What's new in 1.3.0
|
|
176
|
+
|
|
177
|
+
- **`LD`** class — a schema.org JSON-LD generator. Collects structured-data
|
|
178
|
+
nodes during a render and emits them as a single
|
|
179
|
+
`<script type="application/ld+json">` `@graph` in every page's `<head>`.
|
|
180
|
+
- Automatic injection is **opt-in**: add a top-level `jsonld:` block to
|
|
181
|
+
`kirigami.yaml` (even empty, `jsonld: {}`) and an `Organization` (+ `Person`, `WebSite`,
|
|
182
|
+
`WebPage`, `BreadcrumbList`) graph is derived from that block plus the loose
|
|
183
|
+
keys projects already carry (`person`, `jobtitle`, `email`, `area`,
|
|
184
|
+
`knowsabout`, `keywords`, `facebook`, …). No `jsonld:` block → nothing is
|
|
185
|
+
injected.
|
|
186
|
+
- Turn it back off with `jsonld: false` / `jsonld: { auto: false }`, or per
|
|
187
|
+
page with `@ld false`; per-page `@ld_type` / `@ld_title` / `@ld_image` / …
|
|
188
|
+
tags feed the page node, and a `BreadcrumbList` is built from the
|
|
189
|
+
`_index.php` ancestor trail with no opt-in.
|
|
190
|
+
- Explicit builders for everything else: `LD::add()`, `LD::article()`,
|
|
191
|
+
`LD::faqPage()`, `LD::breadcrumb()`, `LD::ref()`, and every schema.org type
|
|
192
|
+
via `LD::typeName([...])`. Procedural aliases: `ld_add()`, `ld_organization()`,
|
|
193
|
+
`ld_script()`, …
|
|
194
|
+
- A page that already hand-writes an `application/ld+json` script is left
|
|
195
|
+
untouched.
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## What's new in 1.2.1
|
|
200
|
+
|
|
201
|
+
- **`processImages()`** JS export — batch resize / palette-extraction through the
|
|
202
|
+
`IMG` class (`src/imagebatch.php`). `@kirigami/kirigami`'s `sass` task now uses
|
|
203
|
+
it for `img-asset()` / `colors()`, so the whole toolchain is free of a native
|
|
204
|
+
image dependency (`sharp` is gone).
|
|
205
|
+
- **`IMG::save()`** takes an optional `$quality` (0-100) for jpg / webp / avif;
|
|
206
|
+
`null` keeps the per-format default (82).
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
94
210
|
## What's new in 1.2.0
|
|
95
211
|
|
|
96
212
|
- **`SCHEMA`** class — a pure-PHP, dependency-free JSON Schema validator
|
|
@@ -98,6 +214,8 @@ Part of the **Kirigami** project ecosystem.
|
|
|
98
214
|
- **`IMG::asset()` / `IMG::palette()`** — static helpers powering kirigami-core's
|
|
99
215
|
`img-asset()` and `colors()` Sass functions: on-demand resize/convert of a
|
|
100
216
|
source image, and cached representative-colour extraction.
|
|
217
|
+
- **`<img asset="…">` tag** — the HTML-side entry point of the image
|
|
218
|
+
autogenerator, same parameters as `IMG::asset()` (see [Built-in tags](#built-in-tags)).
|
|
101
219
|
- **`IMG` now handles vector and exotic formats** — SVG, EPS, AI, PDF (rasterized
|
|
102
220
|
via Imagick), plus HEIC / TIFF / BMP, on top of GD's JPEG / PNG / GIF / WebP /
|
|
103
221
|
AVIF.
|
|
@@ -151,10 +269,10 @@ npm install @kirigami/php-prepros
|
|
|
151
269
|
|
|
152
270
|
Every project **must** have a `kirigami.yaml` at its root. The preprocessor reads it at startup and throws if it is absent or invalid.
|
|
153
271
|
|
|
154
|
-
`@kirigami/php-prepros` itself only acts on
|
|
272
|
+
`@kirigami/php-prepros` itself only acts on four blocks — **`kirigami:`**, **`jsonld:`**, **`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:
|
|
155
273
|
|
|
156
274
|
```yaml
|
|
157
|
-
# yaml-language-server: $schema=https://cdn.jsdelivr.net/
|
|
275
|
+
# yaml-language-server: $schema=https://cdn.jsdelivr.net/gh/php-kirigami/kirigami@main/packages/kirigami/kirigami.schema.json
|
|
158
276
|
```
|
|
159
277
|
|
|
160
278
|
```yaml
|
|
@@ -179,6 +297,10 @@ kirigami:
|
|
|
179
297
|
- keyword one
|
|
180
298
|
- keyword two
|
|
181
299
|
|
|
300
|
+
jsonld: # Presence turns on the LD schema.org JSON-LD generator.
|
|
301
|
+
type: Organization # `jsonld: {}` alone is enough; see the jsonld block below.
|
|
302
|
+
logo: assets/logo.png
|
|
303
|
+
|
|
182
304
|
prepros:
|
|
183
305
|
before: _layouts/header.php # Included before every page body.
|
|
184
306
|
after: _layouts/footer.php # Included after every page body.
|
|
@@ -237,7 +359,17 @@ Core project settings. **Read by `php-prepros`.** The entire block is extracted
|
|
|
237
359
|
| `baseurl` | ✅ | Root URL of the deployed site, no trailing slash. Used to build absolute `<loc>` entries in `sitemap.xml`; exposed as `$baseurl`. |
|
|
238
360
|
| `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. |
|
|
239
361
|
| `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. |
|
|
240
|
-
| *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. |
|
|
362
|
+
| *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 `jsonld` 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`, …). |
|
|
363
|
+
|
|
364
|
+
### `jsonld` block
|
|
365
|
+
|
|
366
|
+
Top-level, optional. Its **presence** switches on the [`LD`](#ld) schema.org
|
|
367
|
+
JSON-LD generator — an `application/ld+json` graph is then injected into every
|
|
368
|
+
page's `<head>`. An empty `jsonld: {}` is enough; its keys refine what `LD`
|
|
369
|
+
otherwise infers from the `kirigami` block and each page's PHPDOC. `jsonld: false`
|
|
370
|
+
(or `jsonld: { auto: false }`) keeps the config values but stops the injection;
|
|
371
|
+
no block at all means nothing is injected. Full key reference and per-page
|
|
372
|
+
`@ld_*` tags: [`LD` → `jsonld` config](#jsonld-config).
|
|
241
373
|
|
|
242
374
|
### `prepros` block
|
|
243
375
|
|
|
@@ -248,13 +380,14 @@ Options for the PHP → HTML compiler. **Read by `php-prepros`.** Declaring this
|
|
|
248
380
|
| `before` | `string` | — | Path (relative to `kirigami.root`) to a PHP file included **before** every page's body. Typically your `<head>`/layout opening. |
|
|
249
381
|
| `after` | `string` | — | Path (relative to `kirigami.root`) to a PHP file included **after** every page's body. Typically your layout closing. |
|
|
250
382
|
| `format` | `bool` | `false` | Pretty-print the compiled HTML via [`HTML::format()`](#html) before writing it to disk. |
|
|
383
|
+
| `head` | `bool` | `true` | Auto-wire each page's `<head>`: a theme/FOUC guard as the first child, a `<link rel="stylesheet">` per `sass` task output, and a `<script>` (no `defer`, before `</body>`) per `esbuild` task output — each with a per-page relative path and a `?<timestamp>` cache-bust. A file already referenced in the page is skipped. Set `false` to disable, or `head: false` on a single `sass`/`esbuild` task to skip just its tag. |
|
|
251
384
|
| `network` | `bool` | `false` | Enables outbound HTTP(S) inside the WASM PHP runtime. Required for PHPDOC `@tag https://…` annotations that fetch remote `.yaml`/`.json`/`.md` data (see [Auto-loading data files](#auto-loading-data-files)), and for the `CURL` / `SCRAPER` classes. |
|
|
252
385
|
| `mountext` | `string[]` | `[]` | Extra file extensions to mount automatically into the virtual filesystem alongside the built-in `.php`, `.json`, `.yaml`, `.yml`, `.md`, `.db`, `.txt`. Use this for assets your PHP code reads directly (e.g. `.svg`, `.webp`). Files with extensions not in this set are skipped during mounting — mount them on demand with [`PREPROS::mount()`](#preprosmountstringarray-patterns) instead. |
|
|
253
386
|
| `includes` | `string[]` | `[]` | PHP files (relative to `kirigami.root`) `include_once`'d once, right after config is loaded — before any page renders. The natural place to `PREPROS::registerTag()`, `PREPROS::registerHook()`, or `MD::registerPlugin()`. |
|
|
254
387
|
|
|
255
388
|
### `image` block
|
|
256
389
|
|
|
257
|
-
Options for the image autogenerator. **Read by `php-prepros`** — these are what [`IMG::asset()` / `IMG::palette()`](#img) (and kirigami-core's `img-asset()` / `colors()` Sass functions
|
|
390
|
+
Options for the image autogenerator. **Read by `php-prepros`** — these are what [`IMG::asset()` / `IMG::palette()`](#img), the [`<img asset>` tag](#img-asset), and kirigami-core's `img-asset()` / `colors()` Sass functions all resolve against. Optional; the defaults below apply even when the block is absent.
|
|
258
391
|
|
|
259
392
|
| Key | Type | Default | Description |
|
|
260
393
|
|-----|------|---------|--------------|
|
|
@@ -355,6 +488,18 @@ All annotations are injected as PHP variables (`$name`, `$title`, `$abstract`,
|
|
|
355
488
|
|
|
356
489
|
Annotations are also available as variables in `before` and `after` PHP included files, so you can write proper metas in the HTML header.
|
|
357
490
|
|
|
491
|
+
Only lines whose first non-whitespace character (past the `*` gutter) is `@`
|
|
492
|
+
open an annotation — an `@word` written in the prose of the block is left alone.
|
|
493
|
+
A value can wrap onto the following **indented** continuation lines:
|
|
494
|
+
|
|
495
|
+
```php
|
|
496
|
+
/**
|
|
497
|
+
* @title About us
|
|
498
|
+
* @description A longer blurb that does not fit comfortably
|
|
499
|
+
* on a single line and continues here.
|
|
500
|
+
*/
|
|
501
|
+
```
|
|
502
|
+
|
|
358
503
|
### Auto-loading data files
|
|
359
504
|
|
|
360
505
|
When an annotation value looks like a filename (with a `.yaml`, `.yml`, `.json`, or `.md` extension), it is automatically parsed and injected as a structured variable instead of a plain string.
|
|
@@ -404,12 +549,65 @@ Two special annotation names change how a page's body is assembled:
|
|
|
404
549
|
*/
|
|
405
550
|
```
|
|
406
551
|
|
|
552
|
+
### Built-in tags
|
|
553
|
+
|
|
554
|
+
Two tags are registered out of the box (`prepros.plugins.php`) and processed
|
|
555
|
+
**after** the PHP runs, on the assembled HTML — no include or plugin needed.
|
|
556
|
+
|
|
557
|
+
#### `<markdown> … </markdown>`
|
|
558
|
+
|
|
559
|
+
Converts its inner content from Markdown to HTML, stripping the common leading
|
|
560
|
+
indentation first (via `STR::trimIndent()`) so you can indent it naturally inside
|
|
561
|
+
your template. All registered [MD plugins](#md-plugins) work inside it. See
|
|
562
|
+
[Extending the `<markdown>` tag](#extending-the-markdown-tag) to override it.
|
|
563
|
+
|
|
564
|
+
```html
|
|
565
|
+
<section>
|
|
566
|
+
<markdown>
|
|
567
|
+
## Who we are
|
|
568
|
+
|
|
569
|
+
We are a **student organization** from Québec.
|
|
570
|
+
</markdown>
|
|
571
|
+
</section>
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
#### `<img asset="…">`
|
|
575
|
+
|
|
576
|
+
The HTML-side entry point of the image autogenerator — the exact same feature as
|
|
577
|
+
the [`img-asset()` Sass function](https://www.npmjs.com/package/@kirigami/kirigami#sass-functions)
|
|
578
|
+
and [`IMG::asset()`](#img), with the same parameters. The tag calls `IMG::asset()`
|
|
579
|
+
under the hood, then swaps the `asset` attribute for the generated `src`.
|
|
580
|
+
|
|
581
|
+
```html
|
|
582
|
+
<!-- in: resolves assets/images/hero.jpg through IMG::asset('hero.jpg', 800, 0, false) -->
|
|
583
|
+
<img asset="hero.jpg" width="800" alt="Our office" loading="lazy">
|
|
584
|
+
<!-- out: <img src="../images/hero-800w.webp" alt="Our office" loading="lazy"> -->
|
|
585
|
+
```
|
|
586
|
+
|
|
587
|
+
| Attribute | Maps to `IMG::asset()` arg | Notes |
|
|
588
|
+
|-----------|---------------------------|-------|
|
|
589
|
+
| `asset` | `$path` | **Required.** Path relative to `image.source`. Missing/empty ⇒ the tag is left untouched. |
|
|
590
|
+
| `width` | `$width` | Optional, integer. Omitted ⇒ `0` (keep). |
|
|
591
|
+
| `height` | `$height` | Optional, integer. Omitted ⇒ `0` (keep). |
|
|
592
|
+
| `cover` | `$cover` | Boolean — **presence means `true`** (crop + fill). |
|
|
593
|
+
| *(any other)* | — | `alt`, `class`, `id`, `loading`, … are passed straight through onto the output `<img>`. |
|
|
594
|
+
|
|
595
|
+
`asset` / `width` / `height` / `cover` are consumed and removed; everything else
|
|
596
|
+
survives. The generated file lands in `image.dest` and is only (re)generated when
|
|
597
|
+
missing or older than the source — see [`IMG`](#img) for the naming convention.
|
|
598
|
+
|
|
599
|
+
> The Sass `img-asset()` / `colors()` functions, the `<img asset>` tag and
|
|
600
|
+
> `IMG::asset()` all run on the **same engine** — the `IMG` class (GD, with the
|
|
601
|
+
> Imagick fallback) in this package. `@kirigami/kirigami`'s `sass` task routes its
|
|
602
|
+
> image work here through [`processImages()`](#processimagesjobs), so there is no
|
|
603
|
+
> native image dependency in the toolchain.
|
|
604
|
+
|
|
407
605
|
---
|
|
408
606
|
|
|
409
607
|
## JavaScript API
|
|
410
608
|
|
|
411
609
|
```js
|
|
412
|
-
import { render, sitemap, runenv, mountPath } from '@kirigami/php-prepros';
|
|
610
|
+
import { render, sitemap, runenv, mountPath, processImages } from '@kirigami/php-prepros';
|
|
413
611
|
```
|
|
414
612
|
|
|
415
613
|
### `render(file?)`
|
|
@@ -493,6 +691,38 @@ Mounting a **directory** only copies files whose extension is one of the default
|
|
|
493
691
|
|
|
494
692
|
**Returns** `Promise<void>`.
|
|
495
693
|
|
|
694
|
+
### `processImages(jobs)`
|
|
695
|
+
|
|
696
|
+
Run a batch of image jobs — resize/encode, or palette extraction — through the
|
|
697
|
+
[`IMG`](#img) class (GD, with the Imagick fallback). This is the engine
|
|
698
|
+
`@kirigami/kirigami`'s `sass` task uses for its `img-asset()` and `colors()`
|
|
699
|
+
functions, so Sass, `IMG::asset()` and the [`<img asset>` tag](#built-in-tags)
|
|
700
|
+
all share one implementation, one `image:` config and one set of output
|
|
701
|
+
filenames — with no native image dependency.
|
|
702
|
+
|
|
703
|
+
```js
|
|
704
|
+
import { processImages } from '@kirigami/php-prepros';
|
|
705
|
+
|
|
706
|
+
const { files, colors } = await processImages([
|
|
707
|
+
// resize/encode `hero.jpg` (resolved against image.source) to each dest —
|
|
708
|
+
// absolute virtual paths, already carrying the target extension
|
|
709
|
+
{ op: 'resize', src: 'hero.jpg', width: 1200, height: 0, cover: false, quality: 82,
|
|
710
|
+
dests: ['/project/src/images/hero-1200w.webp'] },
|
|
711
|
+
|
|
712
|
+
// extract a 5-colour palette (cached in .cache.db); returned, not written
|
|
713
|
+
{ op: 'palette', src: 'hero.jpg', count: 5 },
|
|
714
|
+
]);
|
|
715
|
+
|
|
716
|
+
// files → ['src/images/hero-1200w.webp'] (also copied back to the host)
|
|
717
|
+
// colors → { 'hero.jpg:5': ['#1e3a5f', '#c8a24b', …] }
|
|
718
|
+
```
|
|
719
|
+
|
|
720
|
+
- `jobs` — array of `resize` / `palette` jobs (see the shape above). An **empty
|
|
721
|
+
array is a no-op** and does **not** start the WASM runtime.
|
|
722
|
+
- Staleness is the caller's responsibility: every `resize` job listed is executed.
|
|
723
|
+
|
|
724
|
+
**Returns** `Promise<PreprosResult & { colors: Record<string, string[]> }>`.
|
|
725
|
+
|
|
496
726
|
---
|
|
497
727
|
|
|
498
728
|
## PHP classes reference
|
|
@@ -511,6 +741,7 @@ PREPROS::$config // stdClass — full resolved config; ->data is the ki
|
|
|
511
741
|
// ->image the image: block, plus before/after/format/… from prepros:
|
|
512
742
|
PREPROS::registerTag(string $tag, callable $callback)
|
|
513
743
|
PREPROS::registerHook(string $hook, callable $callback)
|
|
744
|
+
PREPROS::runHook(string $hook, mixed $data = null) // fire a hook (built-in or your own), returns the piped $data
|
|
514
745
|
PREPROS::mount(string|array $patterns)
|
|
515
746
|
PREPROS::exportFile(string|array $absolutePath)
|
|
516
747
|
PREPROS::getExportedFiles(): string[]
|
|
@@ -524,7 +755,7 @@ Internal method called once per source file. Orchestrates the full pipeline:
|
|
|
524
755
|
|
|
525
756
|
1. Resolves PHPDOC metadata and auto-loads data files.
|
|
526
757
|
2. Fires the `pre_render` hook with the raw source contents.
|
|
527
|
-
3. Includes `before.php
|
|
758
|
+
3. Includes `before.php` (wrapped in the `pre_before` / `post_before` hooks), the page body (or `@content`, see [above](#content-and-indent)), and `after.php` (wrapped in `pre_after` / `post_after`) into a single string.
|
|
528
759
|
4. Processes all registered custom HTML tags.
|
|
529
760
|
5. Fires the `post_render` hook on the assembled HTML.
|
|
530
761
|
6. Optionally pretty-prints via `HTML::format()` (when `format: true`).
|
|
@@ -624,7 +855,7 @@ Pretty-printer for the final HTML output. Used automatically when `format: true`
|
|
|
624
855
|
$formatted = HTML::format(string $html): string;
|
|
625
856
|
```
|
|
626
857
|
|
|
627
|
-
Uses PHP 8.4's `Dom\HTMLDocument` (Lexbor engine) to parse the input and re-serialize it with consistent 4-space indentation. Inline elements, `<script>`, and `<style>` blocks are handled correctly — their content is indented but not reformatted. Boolean HTML5 attributes (`muted`, `autoplay`, `noopener`, etc.) are written without a value.
|
|
858
|
+
Uses PHP 8.4's `Dom\HTMLDocument` (Lexbor engine) to parse the input and re-serialize it with consistent 4-space indentation. Inline elements, `<script>`, and `<style>` blocks are handled correctly — their content is indented but not reformatted. A `<pre><code>` block is shifted to its nesting depth too (relative indentation kept), and the leading run is stripped again before display; a bare `<pre>` and `<textarea>` stay byte-for-byte. Boolean HTML5 attributes (`muted`, `autoplay`, `noopener`, etc.) are written without a value.
|
|
628
859
|
|
|
629
860
|
---
|
|
630
861
|
|
|
@@ -707,6 +938,163 @@ if (!$validator->isValid($data)) {
|
|
|
707
938
|
|
|
708
939
|
---
|
|
709
940
|
|
|
941
|
+
### LD
|
|
942
|
+
|
|
943
|
+
A **schema.org JSON-LD generator**. `LD` accumulates structured-data nodes for
|
|
944
|
+
the page under render and emits them as one
|
|
945
|
+
`<script type="application/ld+json">` block — with an `@graph` when there is more
|
|
946
|
+
than one node — in the `<head>`.
|
|
947
|
+
|
|
948
|
+
#### Automatic mode
|
|
949
|
+
|
|
950
|
+
Opt in by adding a top-level `jsonld:` block to `kirigami.yaml` (a sibling of
|
|
951
|
+
`kirigami:`, not nested under it) — an empty `jsonld: {}` is enough. A
|
|
952
|
+
`post_render` hook then injects a graph built from that block, the loose keys of
|
|
953
|
+
the `kirigami` block, and the current page's PHPDOC:
|
|
954
|
+
|
|
955
|
+
- an `Organization` node (`@id` `#organization`) — `name`/`url`/`description`
|
|
956
|
+
from `project`/`baseurl`/`description`, `sameAs` gathered from every
|
|
957
|
+
recognised social-network URL key (`facebook`, `instagram`, `linkedin`,
|
|
958
|
+
`github`, `youtube`, `mastodon`, …), plus `email`, `telephone`, `areaServed`
|
|
959
|
+
(← `area`), `knowsAbout` (← `knowsabout`), `address`, `logo`, and `founder` →
|
|
960
|
+
the Person node when there is one. `@type` comes from `jsonld.type`;
|
|
961
|
+
- a `Person` node (`#person`) when `person` is set — `name` + `jobTitle`
|
|
962
|
+
(← `jobtitle`) + `email` + `url`, linked to the Organization via `worksFor`;
|
|
963
|
+
- a `WebSite` node (`#website`) — `publisher` → Organization, `inLanguage`,
|
|
964
|
+
`keywords` (← `keywords`), and a `SearchAction` when `jsonld.search` is set;
|
|
965
|
+
- a `WebPage` node for the page — see the per-page tags below;
|
|
966
|
+
- a `BreadcrumbList` for every non-home page, derived from the `_index.php`
|
|
967
|
+
ancestor trail (home → each parent section → this page). No `@breadcrumb`
|
|
968
|
+
opt-in needed — it is always attempted while the `jsonld:` block is on.
|
|
969
|
+
Disable it for one page with `@ld_breadcrumb false`.
|
|
970
|
+
|
|
971
|
+
Remove the `jsonld:` block (or set `jsonld: false` / `jsonld: { auto: false }`)
|
|
972
|
+
to stop the automatic pass. A page whose rendered `<head>` already contains an
|
|
973
|
+
`application/ld+json` script is never touched, so hand-rolled markup keeps
|
|
974
|
+
working.
|
|
975
|
+
|
|
976
|
+
**Per-page PHPDOC tags** — these feed the page node (and override the generic
|
|
977
|
+
`@title` / `@description` / `@datePublished` fallbacks):
|
|
978
|
+
|
|
979
|
+
| Tag | Effect |
|
|
980
|
+
|-----|--------|
|
|
981
|
+
| `@ld false` | Skip JSON-LD for this page entirely (`@ld_ignore true` also works). |
|
|
982
|
+
| `@ld_type <Type>` | `@type` of the page node — `AboutPage`, `ContactPage`, `CollectionPage`, `ProfilePage`, or a content type like `Article`, `Service`, `Recipe`, … (default `WebPage`). Types containing “Page” also get `primaryImageOfPage` + a `breadcrumb` link; others get a plain `image`. |
|
|
983
|
+
| `@ld_title <text>` | Page node `name` (default: `@title`). |
|
|
984
|
+
| `@ld_description <text>` | Page node `description` (default: `@description`). |
|
|
985
|
+
| `@ld_image <path>` | Page image, absolute or relative to `baseurl` (default: `@image` / `@ogimage`). |
|
|
986
|
+
| `@ld_published <date>` | `datePublished` (default: `@datePublished` / `@published` / `@date`). |
|
|
987
|
+
| `@ld_modified <date>` | `dateModified` (default: `@dateModified` / `@modified` / `@updated`). |
|
|
988
|
+
| `@ld_breadcrumb false` | No `BreadcrumbList` for this page. |
|
|
989
|
+
|
|
990
|
+
```php
|
|
991
|
+
/**
|
|
992
|
+
* @title À propos
|
|
993
|
+
* @ld_type AboutPage
|
|
994
|
+
* @ld_title À propos de Humain Humain
|
|
995
|
+
* @ld_description Notre approche ethnographique de la consultation.
|
|
996
|
+
*/
|
|
997
|
+
```
|
|
998
|
+
|
|
999
|
+
#### Explicit builders
|
|
1000
|
+
|
|
1001
|
+
Call these from a page template or from a `prepros.includes` file. Nodes added
|
|
1002
|
+
this way are always emitted — with or without a `jsonld:` block — and share the
|
|
1003
|
+
graph the automatic pass uses, so the two combine; a node with a stable `@id` is
|
|
1004
|
+
merged on repeat calls.
|
|
1005
|
+
|
|
1006
|
+
```php
|
|
1007
|
+
LD::add(string|array $type, array $props = [], ?string $id = null): array // build + register a node
|
|
1008
|
+
LD::node(string|array $type, array $props = []): array // build only, no register
|
|
1009
|
+
LD::push(array $node): array // register a ready-made node
|
|
1010
|
+
LD::ref(string $id): array // ['@id' => …] ('#person' → the Person node)
|
|
1011
|
+
LD::remove(string $id): void
|
|
1012
|
+
LD::graph(): array
|
|
1013
|
+
LD::reset(): void
|
|
1014
|
+
|
|
1015
|
+
LD::organization(array $overrides = []): array // config-aware, @id #organization
|
|
1016
|
+
LD::person(array $overrides = []): array // config-aware, @id #person
|
|
1017
|
+
LD::website(array $overrides = []): array // config-aware, @id #website
|
|
1018
|
+
LD::webPage(array $overrides = []): array // current-page-aware, @id …#webpage
|
|
1019
|
+
LD::breadcrumb(?array $items = null, array $overrides = []): array // items: [['name'=>…,'url'=>…], …]
|
|
1020
|
+
LD::faqPage(array $qa, array $overrides = []): array // qa: ['Question ?' => 'Answer.', …]
|
|
1021
|
+
|
|
1022
|
+
LD::address(array|string $a): array
|
|
1023
|
+
LD::image(string $url, ?string $id = null, ?int $w = null, ?int $h = null): array
|
|
1024
|
+
LD::geo(float $lat, float $lng): array
|
|
1025
|
+
LD::rating(int|float $value, ?int $count = null, $best = 5, $worst = 1): array
|
|
1026
|
+
LD::offer(array $o): array
|
|
1027
|
+
LD::contactPoint(array $c): array
|
|
1028
|
+
LD::searchAction(string $urlTemplate): array
|
|
1029
|
+
|
|
1030
|
+
LD::script(bool $pretty = true): string // <script>…</script>, and disables auto-injection
|
|
1031
|
+
LD::json(bool $pretty = true): string // the document, no wrapper
|
|
1032
|
+
```
|
|
1033
|
+
|
|
1034
|
+
Every other schema.org type is reachable through `__callStatic` — the method
|
|
1035
|
+
name is upper-cased to form the `@type`:
|
|
1036
|
+
|
|
1037
|
+
```php
|
|
1038
|
+
LD::recipe([ 'name' => 'Tarte aux pommes', 'recipeYield' => '6', 'prepTime' => 'PT30M' ]);
|
|
1039
|
+
LD::event([ 'name' => 'Vernissage', 'startDate' => '2026-10-01T18:00' ]);
|
|
1040
|
+
LD::softwareApplication([ 'name' => 'Kirigami', 'applicationCategory' => 'DeveloperApplication' ]);
|
|
1041
|
+
|
|
1042
|
+
LD::article([
|
|
1043
|
+
'headline' => $title,
|
|
1044
|
+
'datePublished' => '2026-09-01',
|
|
1045
|
+
'author' => LD::ref('#person'),
|
|
1046
|
+
'image' => LD::image('images/cover.webp'),
|
|
1047
|
+
'publisher' => LD::ref('#organization'),
|
|
1048
|
+
]);
|
|
1049
|
+
```
|
|
1050
|
+
|
|
1051
|
+
Same API from procedural code: `ld_add()`, `ld_node()`, `ld_ref()`,
|
|
1052
|
+
`ld_organization()`, `ld_person()`, `ld_website()`, `ld_web_page()`,
|
|
1053
|
+
`ld_breadcrumb()`, `ld_faq_page()`, `ld_script()`, `ld_json()`.
|
|
1054
|
+
|
|
1055
|
+
#### `jsonld` config
|
|
1056
|
+
|
|
1057
|
+
`jsonld:` is a **top-level** block of `kirigami.yaml` (a sibling of `kirigami:`,
|
|
1058
|
+
`prepros:`, …), and its presence is what **switches automatic injection on**. An
|
|
1059
|
+
empty `jsonld: {}` is enough — everything is then derived from the `kirigami`
|
|
1060
|
+
block's loose keys. Adding keys overrides those inferences; all are optional.
|
|
1061
|
+
|
|
1062
|
+
```yaml
|
|
1063
|
+
kirigami:
|
|
1064
|
+
project: Humain Humain
|
|
1065
|
+
baseurl: https://humainhumain.com
|
|
1066
|
+
person: Méralie Murray-Hall
|
|
1067
|
+
jobtitle: Anthropologue
|
|
1068
|
+
facebook: https://www.facebook.com/humainhumainconsultation.ethnographie/
|
|
1069
|
+
|
|
1070
|
+
jsonld: # top-level; the block being present is
|
|
1071
|
+
type: ProfessionalService # the switch — `jsonld: {}` also works
|
|
1072
|
+
lang: fr-CA # inLanguage on WebSite / WebPage (default: en)
|
|
1073
|
+
logo: assets/logo.png # absolute, or relative to baseurl
|
|
1074
|
+
knowsAbout: [Ethnographie, Recherche qualitative]
|
|
1075
|
+
address:
|
|
1076
|
+
addressLocality: Québec
|
|
1077
|
+
addressCountry: CA
|
|
1078
|
+
search: https://humainhumain.com/?q={search_term_string}
|
|
1079
|
+
```
|
|
1080
|
+
|
|
1081
|
+
| Key | Type | Description |
|
|
1082
|
+
|-----|------|-------------|
|
|
1083
|
+
| `auto` | `bool` | Inject the `<script>` automatically. Default `true` **once the `jsonld:` block exists**. Set `auto: false` (or `jsonld: false`) to keep the block for its config values but stop the automatic injection — `LD::script()` / `ld_script()` can still place it by hand. |
|
|
1084
|
+
| `type` | `string` | `@type` for the main entity — `Organization`, `ProfessionalService`, `LocalBusiness`, … |
|
|
1085
|
+
| `name` / `url` / `description` | `string` | Main-entity / WebSite fields. Default to `project` / `baseurl` / `description`. |
|
|
1086
|
+
| `logo` / `image` | `string` | Absolute URL or path relative to `baseurl`. `image` defaults to `logo`. |
|
|
1087
|
+
| `sameAs` | `string[]` | Profile URLs, merged with the social-network URL keys found loose in the block. |
|
|
1088
|
+
| `email` / `telephone` | `string` | Default to the loose `email` / `telephone` keys. |
|
|
1089
|
+
| `address` | `map` | `PostalAddress` properties. |
|
|
1090
|
+
| `areaServed` | `string` | Defaults to the loose `area` key. |
|
|
1091
|
+
| `knowsAbout` / `keywords` | `string[]` | Default to the loose `knowsabout` / `keywords` keys. |
|
|
1092
|
+
| `person` | `string` \| `map` | The `#person` node. A string is the name; a map takes any `Person` property. Defaults to `person` + `jobtitle` + `email`. |
|
|
1093
|
+
| `lang` | `string` | BCP-47 tag for `inLanguage`. Default `en`. |
|
|
1094
|
+
| `search` | `string` | URL template for a sitelinks `SearchAction`; must contain `{search_term_string}`. |
|
|
1095
|
+
|
|
1096
|
+
---
|
|
1097
|
+
|
|
710
1098
|
### CACHE
|
|
711
1099
|
|
|
712
1100
|
Persistent SQLite-backed key-value cache. Survives across incremental builds via `.cache.db` at the project root.
|
|
@@ -747,11 +1135,11 @@ $img->height // int
|
|
|
747
1135
|
|
|
748
1136
|
// Instance methods (resize/save are chainable)
|
|
749
1137
|
$img->resize(int $width, int $height = 0, bool $cover = false): self
|
|
750
|
-
$img->save(string $dest): self
|
|
1138
|
+
$img->save(string $dest, ?int $quality = null): self // quality 0-100 for jpg/webp/avif; null = per-format default (82)
|
|
751
1139
|
$img->getRepresentativeColors(int $count = 5): string[] // ['#rrggbb', …]
|
|
752
1140
|
|
|
753
1141
|
// Static helpers
|
|
754
|
-
IMG::asset(string $path, int $width = 0, int $height = 0, bool $cover = false): string
|
|
1142
|
+
IMG::asset(string $path, int $width = 0, int $height = 0, bool $cover = false): string // same feature as the <img asset> tag and the img-asset() Sass function
|
|
755
1143
|
IMG::palette(string $path, int $colors = 5): string[]
|
|
756
1144
|
```
|
|
757
1145
|
|
|
@@ -760,6 +1148,7 @@ IMG::palette(string $path, int $colors = 5): string[]
|
|
|
760
1148
|
`save()` infers the output format from the file extension (`.jpg`, `.jpeg`, `.png`, `.gif`, `.webp`, `.avif`) and marks the file as a build output.
|
|
761
1149
|
|
|
762
1150
|
```php
|
|
1151
|
+
// Build a 1200×630 cropped Open Graph image next to the original
|
|
763
1152
|
(new IMG('/project/src/images/hero.jpg'))
|
|
764
1153
|
->resize(1200, 630, true)
|
|
765
1154
|
->save('/project/src/images/hero-og.jpg');
|
|
@@ -771,6 +1160,14 @@ and `colors()` Sass functions: they resolve `$path` against `image.source` from
|
|
|
771
1160
|
when missing or stale), or return a `CACHE`-backed list of representative
|
|
772
1161
|
colours. Both are equally usable from your own PHP.
|
|
773
1162
|
|
|
1163
|
+
`IMG::asset()` is the single implementation behind the [`<img asset>` tag](#built-in-tags)
|
|
1164
|
+
too — the tag is just a thin wrapper. Generated files are named after the source
|
|
1165
|
+
plus a dimension suffix: `-<W>w`, `-<H>h`, `-<W>x<H>`, or `-<W>x<H>-cover`, with
|
|
1166
|
+
the `image.format` extension (e.g. `hero.jpg` + `width="800"` → `hero-800w.webp`).
|
|
1167
|
+
`@kirigami/kirigami`'s Sass `img-asset()` / `colors()` functions produce the same
|
|
1168
|
+
files from the same config through this same class, via
|
|
1169
|
+
[`processImages()`](#processimagesjobs) — one engine, no native dependency.
|
|
1170
|
+
|
|
774
1171
|
---
|
|
775
1172
|
|
|
776
1173
|
### FS
|
|
@@ -781,6 +1178,8 @@ Filesystem utilities.
|
|
|
781
1178
|
FS::dig(string $glob): iterable // recursive glob, yields file paths
|
|
782
1179
|
FS::getRelativePath(string $from, string $to): string
|
|
783
1180
|
FS::phpFileInfo(string $file): object|false // parse PHPDOC annotations
|
|
1181
|
+
FS::getChildren(string $backtrace = ''): object[] // child _index.php pages, ordered by @position
|
|
1182
|
+
FS::getBreadcrumb(string $backtrace = ''): object[] // ancestor _index.php pages, top-most first (opt-in via @breadcrumb)
|
|
784
1183
|
FS::rmdir(string $dir, bool $removeSelf = true): bool
|
|
785
1184
|
FS::pathJoin(string ...$parts): string // URL-aware path join with .. resolution
|
|
786
1185
|
```
|
|
@@ -789,6 +1188,24 @@ FS::pathJoin(string ...$parts): string // URL-aware path join with .. resoluti
|
|
|
789
1188
|
|
|
790
1189
|
`FS::phpFileInfo()` parses the first PHPDOC block of a PHP file and returns its `@tag value` pairs as a `stdClass`. This is used internally to resolve page metadata and data-file annotations.
|
|
791
1190
|
|
|
1191
|
+
`FS::getChildren()` (procedural: `fs_get_children()`) — usable only during a render — scans the folders directly below the calling template, keeps the ones that contain an `_index.php`, and returns one `stdClass` per child: the parsed PHPDOC of that `_index.php` plus a `->file` key with its absolute path. Entries are ordered by `@position` ascending (a page with no `@position` sorts as `999999`), then by folder name (natural, case-insensitive). Handy for building a section index or a navigation menu:
|
|
1192
|
+
|
|
1193
|
+
```php
|
|
1194
|
+
<?php foreach (fs_get_children() as $page): ?>
|
|
1195
|
+
<li><a href="<?= FS::getRelativePath(__DIR__, dirname($page->file)) ?>/"><?= $page->title ?></a></li>
|
|
1196
|
+
<?php endforeach ?>
|
|
1197
|
+
```
|
|
1198
|
+
|
|
1199
|
+
`FS::getBreadcrumb()` (procedural: `fs_get_breadcrumb()`) — also render-only — is the upward counterpart: it returns the calling page's breadcrumb trail. It only produces output when the calling file opts in with `@breadcrumb true` (or `@breadcrumb 1`) in its first PHPDOC block, otherwise it returns `[]`. From the folder **above** the caller's own folder (a page is never part of its own trail), it walks the parent directories upward and collects the `_index.php` of each, stopping at the source root or at the first ancestor `_index.php` that carries no active `@breadcrumb` tag — that page acts as a separator and is left out. Directories without an `_index.php` are skipped without breaking the chain. Entries come back ordered from the top-most ancestor down to the nearest parent, each a `stdClass` (parsed PHPDOC of its `_index.php` plus a `->file` key with its absolute path):
|
|
1200
|
+
|
|
1201
|
+
```php
|
|
1202
|
+
<nav aria-label="Breadcrumb">
|
|
1203
|
+
<?php foreach (fs_get_breadcrumb() as $crumb): ?>
|
|
1204
|
+
<a href="<?= FS::getRelativePath(__DIR__, dirname($crumb->file)) ?>/"><?= $crumb->title ?></a>
|
|
1205
|
+
<?php endforeach ?>
|
|
1206
|
+
</nav>
|
|
1207
|
+
```
|
|
1208
|
+
|
|
792
1209
|
---
|
|
793
1210
|
|
|
794
1211
|
### STR
|
|
@@ -923,6 +1340,53 @@ your own code; the polyfill is there so third-party snippets that call
|
|
|
923
1340
|
|
|
924
1341
|
---
|
|
925
1342
|
|
|
1343
|
+
### Procedural shortcuts (aliases)
|
|
1344
|
+
|
|
1345
|
+
Every static method of every class above is also exposed as a plain function by
|
|
1346
|
+
`src/libraries/aliases.inc.php` (autoloaded — no `require` needed). Each alias is
|
|
1347
|
+
named `<lowercase class>_<snake_case method>()` and does nothing but forward its
|
|
1348
|
+
arguments, so the classes remain the canonical API. They exist to make page
|
|
1349
|
+
templates and `kiri run` scripts read better:
|
|
1350
|
+
|
|
1351
|
+
```php
|
|
1352
|
+
<?= md_to_html(file_get_contents('CHANGELOG.md')) ?>
|
|
1353
|
+
<img src="<?= img_asset('hero.jpg', 1200, 630, true) ?>" alt="">
|
|
1354
|
+
```
|
|
1355
|
+
|
|
1356
|
+
Every function has a complete PHPDoc block, so editor hover and autocomplete
|
|
1357
|
+
surface the signature, parameters, and description.
|
|
1358
|
+
|
|
1359
|
+
| Class | Aliases |
|
|
1360
|
+
|---|---|
|
|
1361
|
+
| `PREPROS` | `prepros_render` · `prepros_sitemap` · `prepros_mount` · `prepros_fstat` · `prepros_export_file` · `prepros_get_exported_files` · `prepros_backtrace_file` · `prepros_register_tag` · `prepros_register_hook` · `prepros_run_hook` |
|
|
1362
|
+
| `MD` | `md_to_html` · `md_register_plugin` · `md_unregister_plugin` · `md_get_registered_plugins` · `md_register_emoji` |
|
|
1363
|
+
| `HTML` | `html_format` |
|
|
1364
|
+
| `YAML` | `yaml_parse` · `yaml_parse_file` · `yaml_load_file` |
|
|
1365
|
+
| `SCHEMA` | `schema` (factory) · `schema_validate` |
|
|
1366
|
+
| `LD` | `ld_add` · `ld_node` · `ld_ref` · `ld_organization` · `ld_person` · `ld_website` · `ld_web_page` · `ld_breadcrumb` · `ld_faq_page` · `ld_script` · `ld_json` |
|
|
1367
|
+
| `CACHE` | `cache_get` · `cache_set` · `cache_delete` · `cache_purge` |
|
|
1368
|
+
| `IMG` | `img_asset` · `img_palette` |
|
|
1369
|
+
| `FS` | `fs_dig` · `fs_get_relative_path` · `fs_php_file_info` · `fs_rmdir` · `fs_path_join` |
|
|
1370
|
+
| `STR` | `str_htmlesc` · `str_replace_tags` · `str_parse_html_attributes` · `str_trim_indent` · `str_is_url` · `str_html_entities_decode` · `str_shorthash` · `str_normalize` · `str_slug` |
|
|
1371
|
+
| `ARR` | `arr_find_key` |
|
|
1372
|
+
| `CURL` | `curl_url_exists` · `curl_get_info` · `curl_get_contents` |
|
|
1373
|
+
| `SCRAPER` | `scraper_get` |
|
|
1374
|
+
| `OBF` | `obf_encode` · `obf_decode` |
|
|
1375
|
+
| `STD` | `std_succeed` · `std_error` |
|
|
1376
|
+
|
|
1377
|
+
Notes:
|
|
1378
|
+
|
|
1379
|
+
- `register_tag()` / `register_hook()` are kept as unprefixed aliases of
|
|
1380
|
+
`prepros_register_tag()` / `prepros_register_hook()`.
|
|
1381
|
+
- `img_asset()` resolves the generated URL relative to the file that calls it,
|
|
1382
|
+
exactly like `IMG::asset()`.
|
|
1383
|
+
- `schema_validate(array $schema, mixed $data, ?array &$errors = null): bool`
|
|
1384
|
+
fills `$errors` with the validation messages.
|
|
1385
|
+
- `yaml_parse()` / `yaml_parse_file()` are only defined when the PECL `yaml`
|
|
1386
|
+
extension isn't already providing them.
|
|
1387
|
+
|
|
1388
|
+
---
|
|
1389
|
+
|
|
926
1390
|
## Plugin system
|
|
927
1391
|
|
|
928
1392
|
`@kirigami/php-prepros` has two complementary plugin layers: **PREPROS** (HTML-tag level, operates on the assembled page) and **MD** (shortcode level, operates inside Markdown content).
|
|
@@ -962,7 +1426,8 @@ The callback receives:
|
|
|
962
1426
|
| `$attrs` | `array` | Parsed HTML attributes as an associative array |
|
|
963
1427
|
| `$body` | `string` | Inner content between opening and closing tags |
|
|
964
1428
|
|
|
965
|
-
The built-in `<markdown>`
|
|
1429
|
+
The built-in [`<markdown>` and `<img asset>` tags](#built-in-tags) are registered
|
|
1430
|
+
this way.
|
|
966
1431
|
|
|
967
1432
|
---
|
|
968
1433
|
|
|
@@ -976,12 +1441,23 @@ PREPROS::registerHook(string $hookName, callable $callback): void
|
|
|
976
1441
|
|
|
977
1442
|
| Hook | When it fires | `$data` type | Expected return |
|
|
978
1443
|
|------|---------------|--------------|-----------------|
|
|
979
|
-
| `
|
|
1444
|
+
| `boot` | Once per process, right after bootstrap (config loaded, `includes` pulled in), before any page renders. Fires for every entrypoint. | `stdClass $config` | ignored |
|
|
1445
|
+
| `page_info` | After PHPDOC parsing, before rendering (auto-loads `.yaml`/`.json`/`.md` annotations) | `[$filePath, $pageObject]` — see note | `$pageObject` (modified) |
|
|
980
1446
|
| `pre_render` | Before PHP execution | Raw file contents as `string` | `string` |
|
|
1447
|
+
| `pre_before` | Just before the `before` include (inside its output buffer — `echo` to prepend to the header) | `before` config path as `string\|null` | ignored |
|
|
1448
|
+
| `post_before` | Right after the `before` include, on the captured header | Header `string` | `string` |
|
|
1449
|
+
| `pre_after` | Just before the `after` include (inside its output buffer — `echo` to prepend to the footer) | `after` config path as `string\|null` | ignored |
|
|
1450
|
+
| `post_after` | Right after the `after` include, on the captured footer | Footer `string` | `string` |
|
|
981
1451
|
| `post_render` | After tag processing, before `HTML::format()` | Assembled HTML `string` | `string` |
|
|
982
1452
|
|
|
983
1453
|
Multiple callbacks can be registered for the same hook — they are executed in registration order, each receiving the return value of the previous one.
|
|
984
1454
|
|
|
1455
|
+
> **`page_info` payload shape.** The hook *fires* with `[$filePath, $pageObject]`,
|
|
1456
|
+
> but each callback is expected to return the `$pageObject` alone — so a callback
|
|
1457
|
+
> registered after the built-ins receives the bare object, not the pair. Handle
|
|
1458
|
+
> both: `$page = is_array($p) ? $p[1] : $p;` (the current page path is always
|
|
1459
|
+
> available as `PREPROS::$file`).
|
|
1460
|
+
|
|
985
1461
|
```php
|
|
986
1462
|
// Example: inject a last-modified date into every page
|
|
987
1463
|
PREPROS::registerHook('post_render', function (string $html): string {
|
|
@@ -1071,7 +1547,10 @@ Read a book
|
|
|
1071
1547
|
|
|
1072
1548
|
## Extending the `<markdown>` tag
|
|
1073
1549
|
|
|
1074
|
-
The `<markdown>` tag is
|
|
1550
|
+
The `<markdown>` tag is one of the [built-in tags](#built-in-tags) (alongside
|
|
1551
|
+
`<img asset>`), registered as a PREPROS tag out of the box. It converts its inner
|
|
1552
|
+
content from Markdown to HTML and strips common leading indentation so you can
|
|
1553
|
+
write cleanly inside your PHP templates:
|
|
1075
1554
|
|
|
1076
1555
|
```html
|
|
1077
1556
|
<section class="about">
|