create-prisma-php-app 5.1.0-alpha.26 → 5.1.0-alpha.27

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.
@@ -1572,6 +1572,7 @@ try {
1572
1572
  MainLayout::$html = TemplateCompiler::compile(MainLayout::$html);
1573
1573
  MainLayout::$html = TemplateCompiler::injectDynamicContent(MainLayout::$html);
1574
1574
  MainLayout::$html = Bootstrap::applyRootLayoutId(MainLayout::$html);
1575
+ MainLayout::$html = TemplateCompiler::deferComponentRoots(MainLayout::$html);
1575
1576
 
1576
1577
  MainLayout::$html = "<!DOCTYPE html>\n" . MainLayout::$html;
1577
1578
 
@@ -0,0 +1,147 @@
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Tests;
6
+
7
+ use PHPUnit\Framework\TestCase;
8
+ use PP\PHPX\TemplateCompiler;
9
+
10
+ /**
11
+ * The server-deferred component-root contract, as the app depends on it.
12
+ *
13
+ * The final document wraps every outermost `[pp-component]` root inside
14
+ * `<body>` in an inert `<template pp-component="...">`. The browser never
15
+ * executes scripts or parses `{...}` placeholders inside a template, so
16
+ * component scripts cannot run before the PulsePoint runtime module loads
17
+ * ("pp is not defined") and raw placeholders cannot trigger bogus requests
18
+ * or value coercion before hydration. `pp.mount()` materializes
19
+ * `template[pp-component]` back into live DOM before scanning for roots.
20
+ */
21
+ final class DeferComponentRootsTest extends TestCase
22
+ {
23
+ private function document(string $bodyContent): string
24
+ {
25
+ return "<html>\n<head><title>t</title></head>\n<body>\n"
26
+ . $bodyContent
27
+ . "\n</body>\n</html>";
28
+ }
29
+
30
+ public function testWrapsRouteRootInInertTemplate(): void
31
+ {
32
+ $html = $this->document(
33
+ '<div pp-component="page_abc123">'
34
+ . '<p>Count: {count}</p>'
35
+ . '<script>const [count, setCount] = pp.state(0);</script>'
36
+ . '</div>'
37
+ );
38
+
39
+ $deferred = TemplateCompiler::deferComponentRoots($html);
40
+
41
+ self::assertStringContainsString(
42
+ '<template pp-component="page_abc123"><div pp-component="page_abc123">',
43
+ $deferred
44
+ );
45
+ self::assertStringContainsString('</div></template>', $deferred);
46
+ // The component script rides along inside the inert template, unchanged.
47
+ self::assertStringContainsString(
48
+ '<script>const [count, setCount] = pp.state(0);</script>',
49
+ $deferred
50
+ );
51
+ }
52
+
53
+ public function testOnlyOutermostBoundaryIsWrapped(): void
54
+ {
55
+ $html = $this->document(
56
+ '<div pp-component="layout_1">'
57
+ . '<div pp-component="page_2"><p>{msg}</p></div>'
58
+ . '</div>'
59
+ );
60
+
61
+ $deferred = TemplateCompiler::deferComponentRoots($html);
62
+
63
+ self::assertSame(1, substr_count($deferred, '<template pp-component='));
64
+ self::assertStringContainsString('<template pp-component="layout_1">', $deferred);
65
+ // The nested boundary stays a plain element inside the inert content.
66
+ self::assertStringContainsString('<div pp-component="page_2">', $deferred);
67
+ }
68
+
69
+ public function testWrapsFooterComponentScript(): void
70
+ {
71
+ $html = $this->document(
72
+ '<div pp-component="page_1"><p>hi</p></div>'
73
+ . '<script pp-component="s9footer">pp.state(1);</script>'
74
+ );
75
+
76
+ $deferred = TemplateCompiler::deferComponentRoots($html);
77
+
78
+ self::assertStringContainsString(
79
+ '<template pp-component="s9footer"><script pp-component="s9footer">pp.state(1);</script></template>',
80
+ $deferred
81
+ );
82
+ }
83
+
84
+ public function testSiblingBoundariesAreEachWrapped(): void
85
+ {
86
+ $html = $this->document(
87
+ '<main><div pp-component="a"><span>x</span></div>'
88
+ . '<div pp-component="b"><span>y</span></div></main>'
89
+ );
90
+
91
+ $deferred = TemplateCompiler::deferComponentRoots($html);
92
+
93
+ self::assertStringContainsString('<template pp-component="a"><div pp-component="a">', $deferred);
94
+ self::assertStringContainsString('<template pp-component="b"><div pp-component="b">', $deferred);
95
+ }
96
+
97
+ public function testEscapedBraceEntitiesGainOneEncodingLayer(): void
98
+ {
99
+ // The browser's parse of the template content consumes one entity
100
+ // layer; the extra `&amp;` keeps the escape visible to the runtime.
101
+ $html = $this->document(
102
+ '<div pp-component="page_1"><code>&#123;literal&#125;</code>'
103
+ . '<script>const s = "&#123;";</script></div>'
104
+ );
105
+
106
+ $deferred = TemplateCompiler::deferComponentRoots($html);
107
+
108
+ self::assertStringContainsString('<code>&amp;#123;literal&amp;#125;</code>', $deferred);
109
+ // Script content is raw text in the template parse — left untouched.
110
+ self::assertStringContainsString('const s = "&#123;";', $deferred);
111
+ }
112
+
113
+ public function testHeadContentIsNeverWrapped(): void
114
+ {
115
+ $html = "<html>\n<head><meta name=\"x\" pp-component=\"nope\"></head>\n<body>\n"
116
+ . '<div pp-component="page_1"><p>hi</p></div>'
117
+ . "\n</body>\n</html>";
118
+
119
+ $deferred = TemplateCompiler::deferComponentRoots($html);
120
+
121
+ self::assertStringNotContainsString('<template pp-component="nope">', $deferred);
122
+ self::assertStringContainsString('<template pp-component="page_1">', $deferred);
123
+ }
124
+
125
+ public function testAlreadyDeferredTemplateIsLeftAlone(): void
126
+ {
127
+ $html = $this->document(
128
+ '<template pp-component="page_1"><div pp-component="page_1"><p>hi</p></div></template>'
129
+ );
130
+
131
+ self::assertSame($html, TemplateCompiler::deferComponentRoots($html));
132
+ }
133
+
134
+ public function testDocumentWithoutBoundariesIsUnchanged(): void
135
+ {
136
+ $html = $this->document('<div><p>plain</p></div>');
137
+
138
+ self::assertSame($html, TemplateCompiler::deferComponentRoots($html));
139
+ }
140
+
141
+ public function testUnbalancedBoundaryMarkupLeavesDocumentUntouched(): void
142
+ {
143
+ $html = $this->document('<div pp-component="page_1"><p>never closed');
144
+
145
+ self::assertSame($html, TemplateCompiler::deferComponentRoots($html));
146
+ }
147
+ }
@@ -39,6 +39,7 @@ One file per surface, flat in `tests/`:
39
39
  | `FeaturesTest.php` | The suite's own feature awareness mirrors `prisma-php.json` |
40
40
  | `CsrfTest.php` | The `pp_csrf` cookie family and `X-CSRF-Token` validation |
41
41
  | `RpcWireContractTest.php` | PulsePoint wire headers (`$isRpc` / `$isNavigation` / `$isWire`), including the pin that the legacy header stays dead |
42
+ | `DeferComponentRootsTest.php` | Server-deferred component roots: outermost `[pp-component]` body roots ship inside inert `<template pp-component>` wrappers |
42
43
  | `SocketTest.php` | One socket connection: JSON frames, reserved error shape, close semantics |
43
44
  | `SocketRegistryTest.php` | Named-socket registration and duplicate refusal |
44
45
  | `SocketPoolTest.php` | Broadcast and pruning |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-prisma-php-app",
3
- "version": "5.1.0-alpha.26",
3
+ "version": "5.1.0-alpha.27",
4
4
  "description": "Prisma-PHP: A Revolutionary Library Bridging PHP with Prisma ORM",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",