Plugins in data/plugins are loaded at startup and can add a tab with a read-only node board, and the demo plugin shows every asset as a node (#113)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
2a390e52ff
commit
57a97ba94f
22 files changed
+1153
-57
No files matched your search
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* The editor plugins in `data/plugins/`: one ES module per `.js` file, and every file there is
|
||||
* active. Read-only for now – putting a file there is done by hand, by the build and by the e2e
|
||||
* seeder. The folder is not served statically: a module is only ever handed out by its bare file
|
||||
* name, so no request can reach past it.
|
||||
*/
|
||||
final class PluginStore
|
||||
{
|
||||
public function __construct(private readonly string $dir)
|
||||
{
|
||||
}
|
||||
|
||||
/** A file name as it may be asked for: no path, no leading dot, `.js` at the end. */
|
||||
public static function isValidName(string $name): bool
|
||||
{
|
||||
return (bool) preg_match('/^[A-Za-z0-9][A-Za-z0-9._-]*\.js$/', $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorted by file name, which is also the order the editor activates them in.
|
||||
*
|
||||
* @return list<array{file: string, bytes: int, updatedAt: int}>
|
||||
*/
|
||||
public function list(): array
|
||||
{
|
||||
$files = is_dir($this->dir) ? scandir($this->dir) : [];
|
||||
$out = [];
|
||||
foreach ($files === false ? [] : $files as $file) {
|
||||
$path = "{$this->dir}/{$file}";
|
||||
if (!self::isValidName($file) || !is_file($path)) {
|
||||
continue;
|
||||
}
|
||||
$out[] = ['file' => $file, 'bytes' => (int) filesize($path), 'updatedAt' => (int) filemtime($path)];
|
||||
}
|
||||
usort($out, static fn (array $a, array $b): int => strcmp($a['file'], $b['file']));
|
||||
return $out;
|
||||
}
|
||||
|
||||
/** The module's source, or null when there is no such plugin. */
|
||||
public function read(string $file): ?string
|
||||
{
|
||||
$path = "{$this->dir}/{$file}";
|
||||
if (!self::isValidName($file) || !is_file($path)) {
|
||||
return null;
|
||||
}
|
||||
$source = file_get_contents($path);
|
||||
return $source === false ? null : $source;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user