@kirigami/php-prepros 1.1.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/index.d.ts CHANGED
@@ -9,7 +9,7 @@
9
9
  *
10
10
  * @example
11
11
  * ```js
12
- * import { render, sitemap } from '@kirigami/php-prepros';
12
+ * import { render, sitemap, runenv, mountPath } from '@kirigami/php-prepros';
13
13
  *
14
14
  * // Compile a single page
15
15
  * const result = await render('src/index.php');
@@ -19,6 +19,18 @@
19
19
  *
20
20
  * // Generate sitemap.xml
21
21
  * const sitemap = await sitemap();
22
+ *
23
+ * // Run an arbitrary PHP script in the same sandboxed environment
24
+ * const result = await runenv('scripts/purge-cache.php');
25
+ *
26
+ * // Mount a local file/directory into the sandbox before rendering
27
+ * await mountPath('assets/data/team.yaml');
28
+ *
29
+ * // Resize images / extract palettes through the IMG class (GD + Imagick)
30
+ * const { files, colors } = await processImages([
31
+ * { op: 'resize', src: 'hero.jpg', width: 1200, dests: ['/project/src/images/hero-1200w.webp'] },
32
+ * { op: 'palette', src: 'hero.jpg', count: 5 },
33
+ * ]);
22
34
  * ```
23
35
  */
24
36
 
@@ -106,4 +118,135 @@ export function render(file?: string): Promise<PreprosResult>;
106
118
  * @returns A {@link PreprosResult} with `files` containing the path to the
107
119
  * generated `sitemap.xml`.
108
120
  */
109
- export function sitemap(dir?: string): Promise<PreprosResult>;
121
+ export function sitemap(dir?: string): Promise<PreprosResult>;
122
+
123
+
124
+ /**
125
+ * Run an arbitrary PHP script — not a page template — inside the same
126
+ * sandboxed WASM environment used by {@link render}, with the full
127
+ * `php-prepros` class library autoloaded and `kirigami.yaml`'s `kirigami`
128
+ * block available as `PREPROS::$config->data`.
129
+ *
130
+ * Useful for one-off maintenance scripts, data migrations, or CLI-style
131
+ * tooling that needs `CACHE`, `SCRAPER`, `IMG`, etc. without going through
132
+ * the page-rendering pipeline.
133
+ *
134
+ * ```js
135
+ * // Run a standalone PHP script
136
+ * const result = await runenv('scripts/purge-cache.php');
137
+ *
138
+ * // Also mount extra local paths/files into the sandbox before running
139
+ * const result = await runenv('scripts/build-og-images.php', ['assets/photos']);
140
+ *
141
+ * // Extra arguments are appended and available as $argv[2], $argv[3], … in the script
142
+ * const result = await runenv('scripts/import.php', [], '--force');
143
+ * ```
144
+ *
145
+ * @param script Path to a PHP file inside the project, executed with
146
+ * `require_once`.
147
+ * @param paths Extra local paths (files or directories) to mount into the
148
+ * sandbox before the script runs.
149
+ * @param args Extra string arguments appended to the script's `$argv`.
150
+ *
151
+ * @returns A {@link PreprosResult} describing what was written. Call
152
+ * `PREPROS::exportFile()` inside the script for any file you want
153
+ * listed in `result.files`.
154
+ *
155
+ * @throws When no `script` path is given.
156
+ * @throws When `script` resolves outside the project root, or doesn't exist.
157
+ */
158
+ export function runenv(script: string, paths?: string[], ...args: string[]): Promise<PreprosResult>;
159
+
160
+
161
+ /**
162
+ * The JavaScript-side counterpart to `PREPROS::mount()`. Mounts a local
163
+ * file or directory — recursively, preserving structure — into the WASM
164
+ * sandbox's virtual filesystem, ahead of (or between) calls to
165
+ * {@link render}, {@link sitemap}, or {@link runenv}.
166
+ *
167
+ * Mounting a directory only copies files whose extension is one of the
168
+ * defaults (`.php`, `.json`, `.yaml`, `.yml`, `.md`, `.db`, `.txt`) or
169
+ * listed in `prepros.mountext`, same as automatic root mounting. Mounting a
170
+ * single file directly copies it regardless of extension.
171
+ *
172
+ * ```js
173
+ * // Mount a single file at its natural virtual path (/project/<relative path>)
174
+ * await mountPath('assets/data/team.yaml');
175
+ *
176
+ * // Mount a whole directory, at a custom virtual path
177
+ * await mountPath('vendor/fonts', '/project/fonts');
178
+ * ```
179
+ *
180
+ * @param localPath Path to a local file or directory. Relative paths are
181
+ * resolved against the project root.
182
+ * @param virtualDir Destination path inside the WASM filesystem. Defaults
183
+ * to `/project/<localPath relative to the project root>`
184
+ * when omitted.
185
+ * @param php WASM PHP instance to mount into. Defaults to the shared
186
+ * singleton instance (creating it if needed).
187
+ */
188
+ export function mountPath(localPath: string, virtualDir?: string, php?: unknown): Promise<void>;
189
+
190
+
191
+ /**
192
+ * A single unit of work for {@link processImages}.
193
+ *
194
+ * - **`resize`** — decode `src` (resolved against `image.source`), optionally
195
+ * resize it, and encode a copy to every path in `dests` (each an absolute
196
+ * virtual path, e.g. `/project/src/images/hero-800w.webp`, already carrying
197
+ * the target extension). Omitting both `width` and `height` re-encodes at the
198
+ * source size. Staleness must be decided by the caller — every job passed in
199
+ * is executed.
200
+ * - **`palette`** — extract `count` representative colours from `src` via
201
+ * `IMG::palette()` (cached in `.cache.db`); returned in `colors`, not written.
202
+ */
203
+ export type ImageJob =
204
+ | {
205
+ op: "resize";
206
+ /** Source path, relative to `image.source` in `kirigami.yaml`. */
207
+ src: string;
208
+ /** Target width in px. `0`/omitted = derive from height (or keep). */
209
+ width?: number;
210
+ /** Target height in px. `0`/omitted = derive from width (or keep). */
211
+ height?: number;
212
+ /** Crop to fill instead of fitting inside `width`×`height`. */
213
+ cover?: boolean;
214
+ /** Encoder quality (0-100) for lossy formats. Defaults to 82. */
215
+ quality?: number;
216
+ /** Absolute virtual destination paths to write the encoded image to. */
217
+ dests: string[];
218
+ }
219
+ | {
220
+ op: "palette";
221
+ /** Source path, relative to `image.source` in `kirigami.yaml`. */
222
+ src: string;
223
+ /** Number of colours to extract. Defaults to 5. */
224
+ count?: number;
225
+ };
226
+
227
+
228
+ /**
229
+ * Result of {@link processImages}: a {@link PreprosResult} whose `files` lists
230
+ * every image written back to the host, plus the palettes that were requested.
231
+ */
232
+ export interface ImageBatchResult extends PreprosResult {
233
+ /** Extracted palettes, keyed `"<src>:<count>"`, each a list of `#rrggbb`. */
234
+ colors: Record<string, string[]>;
235
+ }
236
+
237
+
238
+ /**
239
+ * Run a batch of image jobs through the same `IMG` class (GD, with the Imagick
240
+ * fallback) used by `IMG::asset()` and the `<img asset>` tag.
241
+ *
242
+ * This is the engine behind `@kirigami/kirigami`'s Sass `img-asset()` and
243
+ * `colors()` functions: one implementation, one `image:` config, one set of
244
+ * output filenames across Sass, PHP and HTML — no native image dependency.
245
+ *
246
+ * @param jobs List of {@link ImageJob}s. An empty list is a no-op (the WASM
247
+ * runtime is not started).
248
+ *
249
+ * @returns An {@link ImageBatchResult}. Resized files are also copied to the
250
+ * host at the project-relative equivalent of each `dests` entry.
251
+ */
252
+ export function processImages(jobs?: ImageJob[]): Promise<ImageBatchResult>;
package/index.js CHANGED
@@ -1 +1 @@
1
- export { render, sitemap, runenv } from "./src/prepros.js";
1
+ export { render, sitemap, runenv, mountPath, processImages } from "./src/prepros.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kirigami/php-prepros",
3
- "version": "1.1.0",
3
+ "version": "1.6.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",
@@ -28,7 +28,7 @@
28
28
  "README.md"
29
29
  ],
30
30
  "engines": {
31
- "node": ">=20.10.0",
31
+ "node": ">=24.0.0",
32
32
  "npm": ">=10.2.3"
33
33
  },
34
34
  "exports": {
@@ -44,8 +44,8 @@
44
44
  "test": "echo \"Error: no test specified\" && exit 1"
45
45
  },
46
46
  "dependencies": {
47
- "@kirigami/php-wasm": "8.5.10-3",
48
- "@kirigami/struct-walker": "1.0.3",
47
+ "@kirigami/php-wasm": "8.5.10-5",
48
+ "@kirigami/struct-walker": "1.0.4",
49
49
  "picomatch": "^4.0.7"
50
50
  },
51
51
  "repository": {
@@ -0,0 +1,67 @@
1
+ <?php
2
+
3
+ /**
4
+ * Batch image worker for the Kirigami build pipeline.
5
+ *
6
+ * Driven by @kirigami/kirigami's `sass` task (see processImages() in
7
+ * prepros.js): it receives a JSON list of jobs on $argv[2] and runs them
8
+ * through the IMG class (GD, with the Imagick fallback) so that the Sass
9
+ * `img-asset()` / `colors()` functions and the PHP `IMG::asset()` /
10
+ * `<img asset>` tag all share one engine.
11
+ *
12
+ * Job shapes:
13
+ * { "op": "resize", "src": "hero.jpg", "width": 800, "height": 0,
14
+ * "cover": false, "quality": 82,
15
+ * "dests": ["/project/dist/images/hero-800w.webp",
16
+ * "/project/src/images/hero-800w.webp"] }
17
+ * { "op": "palette", "src": "hero.jpg", "count": 5 }
18
+ *
19
+ * `src` is resolved against `image.source`; `dests` are absolute virtual
20
+ * paths already carrying the target extension. Staleness is decided on the
21
+ * JS side, so every job listed here is meant to run. Generated files are
22
+ * returned through PREPROS::exportFile() (STD::succeed copies them back to
23
+ * the host); extracted palettes ride back in the `colors` map.
24
+ */
25
+
26
+ include(__DIR__ . '/utils.inc.php');
27
+
28
+ try {
29
+
30
+ $jobs = json_decode($argv[2] ?? '[]');
31
+ if (!is_array($jobs)) throw new Exception("Invalid jobs payload.");
32
+
33
+ $colors = [];
34
+
35
+ foreach ($jobs as $job) {
36
+
37
+ if (($job->op ?? '') === 'palette') {
38
+ $count = (int) ($job->count ?? 5);
39
+ $colors[$job->src . ':' . $count] = IMG::palette($job->src, $count);
40
+ continue;
41
+ }
42
+
43
+ // op: resize
44
+ $srcfile = FS::pathJoin(PREPROS::$config->image->source, $job->src);
45
+ if (!$mounted = PREPROS::mount($srcfile)) throw new Exception("Can't mount image: {$job->src}");
46
+ $localfile = current($mounted);
47
+ if (!is_file($localfile)) throw new Exception("Can't mount image: {$job->src}");
48
+
49
+ $width = (int) ($job->width ?? 0);
50
+ $height = (int) ($job->height ?? 0);
51
+ $cover = (bool) ($job->cover ?? false);
52
+ $quality = isset($job->quality) ? (int) $job->quality : null;
53
+
54
+ $img = new IMG($localfile);
55
+ if ($width && $height) $img->resize($width, $height, $cover);
56
+ elseif ($width) $img->resize($width);
57
+ elseif ($height) $img->resize((int) round($img->width * $height / $img->height), $height);
58
+ // neither: no resize, just re-encode to the target format
59
+
60
+ foreach ($job->dests as $dest) $img->save($dest, $quality);
61
+ }
62
+
63
+ STD::succeed(['files' => PREPROS::getExportedFiles(), 'colors' => $colors]);
64
+
65
+ } catch (Throwable $e) {
66
+ STD::error($e->getMessage());
67
+ }