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.
package/dist/bootstrap.php
CHANGED
|
@@ -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 `&` keeps the escape visible to the runtime.
|
|
101
|
+
$html = $this->document(
|
|
102
|
+
'<div pp-component="page_1"><code>{literal}</code>'
|
|
103
|
+
. '<script>const s = "{";</script></div>'
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
$deferred = TemplateCompiler::deferComponentRoots($html);
|
|
107
|
+
|
|
108
|
+
self::assertStringContainsString('<code>&#123;literal&#125;</code>', $deferred);
|
|
109
|
+
// Script content is raw text in the template parse — left untouched.
|
|
110
|
+
self::assertStringContainsString('const s = "{";', $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
|
+
}
|
package/dist/tests/README.md
CHANGED
|
@@ -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 |
|