Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clean up GeneratesDocumentationSearchIndexFile #418

Merged
merged 19 commits into from
Aug 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
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
1 change: 1 addition & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ This release continues refactoring the internal codebase. As part of this, a lar
### Changed
- Blog posts now have the same open graph title format as other pages
- Merged deprecated method `getRoutesForModel` into `getRoutes` in `RouteCollection`
- Cleans up and refactors `GeneratesDocumentationSearchIndexFile`, and marks it as internal
- internal: Inline deprecated internal method usage `getOutputPath` replacing it `Hyde::pages()` helper with in `HydeRebuildStaticSiteCommand`

### Deprecated
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,82 +8,74 @@
use Hyde\Framework\Models\Pages\DocumentationPage;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
use JetBrains\PhpStorm\ArrayShape;

/**
* Generate a JSON file that can be used as a search index for documentation pages.
* @internal Generate a JSON file that can be used as a search index for documentation pages.
*
* @todo Convert into Service, and add more strategies, such as slug-only (no file parsing)
* search which while dumber, would be much faster to compile and take way less space.
* @todo Refactor to use custom site output paths
*
* @see \Hyde\Framework\Testing\Feature\Actions\GeneratesDocumentationSearchIndexFileTest
*
* @phpstan-consistent-constructor
*/
class GeneratesDocumentationSearchIndexFile implements ActionContract
final class GeneratesDocumentationSearchIndexFile implements ActionContract
{
use InteractsWithDirectories;

public Collection $searchIndex;
public static string $filePath = '_site/docs/search.json';

public static function run(): void
public static function run(): self
{
(new static())->execute();
return (new self())->execute();
}

public function __construct()
{
$this->searchIndex = new Collection();

static::$filePath = '_site/'.config('docs.output_directory', 'docs').'/search.json';
static::$filePath = Hyde::pathToRelative(Hyde::getSiteOutputPath(
DocumentationPage::getOutputDirectory().'/search.json')
);
}

public function execute(): void
public function execute(): self
{
$this->generate();
$this->save();

return $this;
}

public function generate(): static
public function generate(): self
{
/** @var DocumentationPage $page */
foreach (DocumentationPage::all() as $page) {
if (! in_array($page->identifier, config('docs.exclude_from_search', []))) {
$this->searchIndex->push(
$this->generatePageObject($page)
$this->generatePageEntry($page)
);
}
}

return $this;
}

public function generatePageObject(DocumentationPage $page): object
#[ArrayShape(['slug' => 'string', 'title' => 'string', 'content' => 'string', 'destination' => 'string'])]
public function generatePageEntry(DocumentationPage $page): array
{
return (object) [
return [
'slug' => basename($page->identifier),
'title' => $page->title,
'content' => trim($this->getSearchContentForDocument($page)),
'destination' => $this->getDestinationForSlug(basename($page->identifier)),
];
}

public function getObject(): object
{
return (object) $this->searchIndex;
}

public function getJson(): string
{
return json_encode($this->getObject());
}

public function save(): static
protected function save(): self
{
$this->needsDirectory(Hyde::path(str_replace('/search.json', '', static::$filePath)));

file_put_contents(Hyde::path(static::$filePath), $this->getJson());
file_put_contents(Hyde::path(static::$filePath), $this->searchIndex->toJson());

return $this;
}
Expand All @@ -110,7 +102,7 @@ public function save(): static
* Returning $document->body as is: 500ms
* Returning $document->body as Str::markdown(): 920ms + 10ms for regex
*/
public function getSearchContentForDocument(DocumentationPage $page): string
protected function getSearchContentForDocument(DocumentationPage $page): string
{
// This is compiles the Markdown body into HTML, and then strips out all
// HTML tags to get a plain text version of the body. This takes a long
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,23 +74,24 @@ public function test_it_handles_generation_even_when_there_are_no_pages()

public function test_save_method_saves_the_file_to_the_correct_location()
{
(new Action())->save();
Action::run();

$this->assertFileExists('_site/docs/search.json');
}

public function test_generate_page_object_method_generates_a_page_object()
public function test_generate_page_entry_method_generates_a_page_entry()
{
$expected = new \stdClass;
$expected->slug = 'foo';
$expected->title = 'Bar';
$expected->content = "Bar \n Hello World";
$expected->destination = 'foo.html';
$expected = [
'slug' => 'foo',
'title' => 'Bar',
'content' => "Bar \n Hello World",
'destination' => 'foo.html',
];

file_put_contents(Hyde::path('_docs/foo.md'), "# Bar\n\n Hello World");

$this->assertEquals(
$expected, (new Action())->generatePageObject(DocumentationPage::parse('foo'))
$expected, (new Action())->generatePageEntry(DocumentationPage::parse('foo'))
);

unlink(Hyde::path('_docs/foo.md'));
Expand All @@ -101,10 +102,11 @@ public function test_it_generates_a_valid_JSON()
file_put_contents(Hyde::path('_docs/foo.md'), "# Bar\n\n Hello World");
file_put_contents(Hyde::path('_docs/bar.md'), "# Foo\n\n Hello World");

$generatesDocumentationSearchIndexFile = (new Action())->generate();
$this->assertEquals(
'[{"slug":"bar","title":"Foo","content":"Foo \n Hello World","destination":"bar.html"},'.
'{"slug":"foo","title":"Bar","content":"Bar \n Hello World","destination":"foo.html"}]',
(new Action())->generate()->getJson()
json_encode($generatesDocumentationSearchIndexFile->searchIndex->toArray())
);

unlink(Hyde::path('_docs/foo.md'));
Expand Down Expand Up @@ -134,7 +136,8 @@ public function test_excluded_pages_are_not_present_in_the_search_index()
Hyde::touch(('_docs/excluded.md'));
config(['docs.exclude_from_search' => ['excluded']]);

$this->assertStringNotContainsString('excluded', (new Action())->generate()->getJson());
$generatesDocumentationSearchIndexFile = (new Action())->generate();
$this->assertStringNotContainsString('excluded', json_encode($generatesDocumentationSearchIndexFile->searchIndex->toArray()));

unlink(Hyde::path('_docs/excluded.md'));
}
Expand All @@ -144,7 +147,8 @@ public function test_nested_source_files_do_not_retain_directory_name_in_search_
mkdir(Hyde::path('_docs/foo'));
touch(Hyde::path('_docs/foo/bar.md'));

$this->assertStringNotContainsString('foo', (new Action())->generate()->getJson());
$generatesDocumentationSearchIndexFile = (new Action())->generate();
$this->assertStringNotContainsString('foo', json_encode($generatesDocumentationSearchIndexFile->searchIndex->toArray()));

unlink(Hyde::path('_docs/foo/bar.md'));
rmdir(Hyde::path('_docs/foo'));
Expand Down