diff --git a/src/Blaze.php b/src/Blaze.php
index bbf9c9f..f622539 100644
--- a/src/Blaze.php
+++ b/src/Blaze.php
@@ -16,6 +16,7 @@
* @method static \Livewire\Blaze\Tokenizer\Tokenizer tokenizer()
* @method static \Livewire\Blaze\Parser\Parser parser()
* @method static \Livewire\Blaze\Folder\Folder folder()
+ * @method static \Livewire\Blaze\Imprinter\Imprinter imprinter()
* @method static array flushFoldedEvents()
* @see \Livewire\Blaze\BlazeManager
*/
diff --git a/src/BlazeManager.php b/src/BlazeManager.php
index 75a663e..d63e017 100644
--- a/src/BlazeManager.php
+++ b/src/BlazeManager.php
@@ -4,6 +4,7 @@
use Livewire\Blaze\Events\ComponentFolded;
use Livewire\Blaze\Nodes\ComponentNode;
+use Livewire\Blaze\Imprinter\Imprinter;
use Livewire\Blaze\Tokenizer\Tokenizer;
use Illuminate\Support\Facades\Event;
use Livewire\Blaze\Memoizer\Memoizer;
@@ -27,6 +28,7 @@ public function __construct(
protected Walker $walker,
protected Folder $folder,
protected Memoizer $memoizer,
+ protected Imprinter $imprinter,
) {
Event::listen(ComponentFolded::class, function (ComponentFolded $event) {
$this->foldedEvents[] = $event;
@@ -109,7 +111,16 @@ public function compile(string $template): string
array_pop($dataStack);
}
- return $this->memoizer->memoize($this->folder->fold($node));
+ foreach ([
+ $this->imprinter->imprint(...),
+ $this->folder->fold(...),
+ $this->imprinter->restore(...),
+ $this->memoizer->memoize(...),
+ ] as $process) {
+ $node = $process($node);
+ }
+
+ return $node;
},
);
@@ -167,4 +178,9 @@ public function folder(): Folder
{
return $this->folder;
}
+
+ public function imprinter(): Imprinter
+ {
+ return $this->imprinter;
+ }
}
diff --git a/src/BlazeServiceProvider.php b/src/BlazeServiceProvider.php
index 98b9776..7e26a0d 100644
--- a/src/BlazeServiceProvider.php
+++ b/src/BlazeServiceProvider.php
@@ -3,6 +3,7 @@
namespace Livewire\Blaze;
use Livewire\Blaze\Directive\BlazeDirective;
+use Livewire\Blaze\Imprinter\Imprinter;
use Livewire\Blaze\Tokenizer\Tokenizer;
use Illuminate\Support\ServiceProvider;
use Livewire\Blaze\Memoizer\Memoizer;
@@ -37,6 +38,9 @@ protected function registerBlazeManager(): void
new Memoizer(
componentNameToPath: fn ($name) => $bladeService->componentNameToPath($name),
),
+ new Imprinter(
+ componentNameToPath: fn ($name) => $bladeService->componentNameToPath($name),
+ ),
));
$this->app->alias(BlazeManager::class, Blaze::class);
diff --git a/src/Imprinter/Imprinter.php b/src/Imprinter/Imprinter.php
new file mode 100644
index 0000000..78daed3
--- /dev/null
+++ b/src/Imprinter/Imprinter.php
@@ -0,0 +1,125 @@
+componentNameToPath = $componentNameToPath;
+ $this->cacheDirectory = storage_path('framework/views/livewire/blaze/components');
+ Blade::anonymousComponentPath($this->cacheDirectory, $this->cacheNamespace);
+ }
+
+ public function imprint(Node $node): Node
+ {
+ if (! $node instanceof ComponentNode) {
+ return $node;
+ }
+
+ $this->capture($node);
+
+ return $node;
+ }
+
+ public function restore(Node $node): Node
+ {
+ if (! $node instanceof TextNode) {
+ return $node;
+ }
+
+ // Look for IMPRINT_PLACEHOLDER throughout $node->content and capture the full string
+ $node->content = preg_replace_callback('/IMPRINT_PLACEHOLDER_[a-zA-Z0-9]{10}/i', function (array $matches) {
+ $imprintPlaceholder = $matches[0];
+
+ $imprint = $this->imprintPlaceholders[$imprintPlaceholder];
+ $attributes = $imprint['attributes'];
+ $content = $imprint['content'];
+
+ foreach ($attributes as $name => $value) {
+ $content = preg_replace('/\$'.$name.'(?![a-zA-Z0-9_])/', '\''.$value.'\'', $content);
+ }
+
+ return $content;
+ }, $node->content);
+
+ return $node;
+ }
+
+ public function getAttributes(string $imprintPlaceholder): array
+ {
+ return $this->imprintPlaceholders[$imprintPlaceholder]['attributes'] ?? [];
+ }
+
+ public function getContent(string $imprintPlaceholder): string
+ {
+ return $this->imprintPlaceholders[$imprintPlaceholder]['content'];
+ }
+
+ public function storeAttributes(string $imprintPlaceholder, array $attributes): void
+ {
+ $this->imprintPlaceholders[$imprintPlaceholder]['attributes'] = $attributes;
+ }
+
+ protected function capture(ComponentNode $node): void
+ {
+ $componentPath = ($this->componentNameToPath)($node->name);
+
+ if (empty($componentPath) || ! file_exists($componentPath)) {
+ return;
+ }
+
+ $source = file_get_contents($componentPath);
+
+ preg_match_all('/(\s*)@imprint\((.*?)\)(.*?)@endimprint/s', $source, $matches);
+
+ if (empty($matches[0])) {
+ return;
+ }
+
+ $modifiedSource = $source;
+
+ foreach ($matches[0] as $index => $match) {
+ $imprintBlock = $matches[0][$index];
+ $whitespace = $matches[1][$index];
+ $attributes = $matches[2][$index];
+ $content = $matches[3][$index];
+
+ $placeholder = 'IMPRINT_PLACEHOLDER_' . str()->random(10);
+
+ $output = $whitespace;
+ $output .= '<'.'?php \Livewire\Blaze\Blaze::imprinter()->storeAttributes(\'' . $placeholder . '\', ' . $attributes . '); ?'.'>';
+ $output .= $placeholder;
+
+ $modifiedSource = str_replace($imprintBlock, $output, $modifiedSource);
+
+ $this->imprintPlaceholders[$placeholder] = [
+ 'attributes' => [],
+ 'content' => $content,
+ ];
+ }
+
+ $name = $node->name;
+ $path = str_replace('.', '/', $name);
+
+ $directory = $this->cacheDirectory . '/' . str($path)->beforeLast('/');
+ $filename = str($path)->afterLast('/')->value() . '.blade.php';
+
+ File::ensureDirectoryExists($directory);
+
+ File::put($directory . '/' . $filename, $modifiedSource);
+
+ $node->name = $this->cacheNamespace . '::' . $name;
+ }
+}
diff --git a/tests/ImprintTest.php b/tests/ImprintTest.php
new file mode 100644
index 0000000..6b62a85
--- /dev/null
+++ b/tests/ImprintTest.php
@@ -0,0 +1,22 @@
+anonymousComponentPath(__DIR__ . '/fixtures/components');
+
+ \Illuminate\Support\Facades\Artisan::call('view:clear');
+ });
+
+ it('can imprint components with nested components that use the error bag', function () {
+ $input = '