Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions framework/core/src/Forum/Content/Discussion.php
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ public function __invoke(Document $document, Request $request): Document

$document->payload['apiDocument'] = $apiDocument;

// The page shows its posts only once these have loaded.
$document->preloadChunk('core', 'forum/components/PostStream');
$document->preloadChunk('core', 'forum/components/PostStreamScrubber');

$document->canonicalUrl = $url([]);
$document->page = $page;
$document->hasNextPage = $hasNextPage;
Expand Down
20 changes: 20 additions & 0 deletions framework/core/src/Frontend/Document.php
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,26 @@ public function render(): string
return $this->makeView()->render();
}

/**
* Preload a JavaScript chunk the page imports as soon as it boots, so it
* downloads alongside the main bundle rather than after it.
*
* @param string $namespace `core`, or the ID of the extension the chunk belongs to.
* @param string $path The chunk's path in its JS, such as `forum/components/PostStream`.
*/
public function preloadChunk(string $namespace, string $path): void
{
// The address the frontend loads it from: see ExportRegistry's versionedChunkUrl.
$file = "js/$namespace/$path.js";
$url = rtrim($this->filesystem->disk('flarum-assets')->url('js'), '/')."/$namespace/$path.js";

if ($revision = $this->versioner->getRevision($file)) {
$url .= "?v=$revision";
}

$this->preloads[] = ['href' => $url, 'as' => 'script'];
}

protected function makeView(): View
{
return $this->view->make($this->appView)->with([
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?php

/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/

namespace Flarum\Tests\integration\forum;

use Flarum\Discussion\Discussion;
use Flarum\Post\Post;
use Flarum\Testing\integration\RetrievesAuthorizedUsers;
use Flarum\Testing\integration\TestCase;
use Flarum\User\User;
use PHPUnit\Framework\Attributes\Test;

class DiscussionChunkPreloadTest extends TestCase
{
use RetrievesAuthorizedUsers;

protected function setUp(): void
{
parent::setUp();

$this->prepareDatabase([
User::class => [
$this->normalUser(),
],
Discussion::class => [
['id' => 1, 'title' => 'Test Discussion', 'user_id' => 1, 'first_post_id' => 1],
],
Post::class => [
['id' => 1, 'discussion_id' => 1, 'user_id' => 2, 'type' => 'comment', 'content' => '<t><p>can i haz potat?</p></t>'],
],
]);
}

private function page(string $path): string
{
$response = $this->send($this->request('GET', $path));

$this->assertEquals(200, $response->getStatusCode());

return (string) $response->getBody();
}

/**
* The address the frontend loads a chunk from (see ExportRegistry's
* versionedChunkUrl), worked out from what the page hands it.
*/
private function chunkUrl(string $html, string $path): string
{
preg_match('/"jsChunksBaseUrl":("[^"]*")/', $html, $base);
preg_match('/<script id="flarum-rev-manifest" type="application\/json">(.*?)<\/script>/s', $html, $manifest);

$file = "core/$path.js";
$revision = json_decode($manifest[1], true)["js/$file"] ?? null;

return json_decode($base[1]).'/'.$file.($revision ? "?v=$revision" : '');
}

#[Test]
public function a_discussion_page_preloads_the_chunks_it_shows_its_posts_with()
{
$html = $this->page('/d/1');

foreach (['forum/components/PostStream', 'forum/components/PostStreamScrubber'] as $chunk) {
$this->assertStringContainsString('<link rel="preload" href="'.e($this->chunkUrl($html, $chunk)).'" as="script">', $html);
}
}

#[Test]
public function other_pages_do_not_preload_them()
{
$this->assertDoesNotMatchRegularExpression('/<link rel="preload" href="[^"]*PostStream/', $this->page('/'));
}
}
Loading