From 6989b3fc4fafd1c4df9814903f323b1e2ec2ef80 Mon Sep 17 00:00:00 2001 From: Mike van Riel Date: Sun, 26 Jan 2020 17:13:13 +0100 Subject: [PATCH 1/2] Allow parse and transform to be ran independently For phpDocumentor we want to be able to build a TOC for the API documentation and the output of one or more runs of this RST parser and inject that into the Twig templates. In the existing setup, this causes a chicken and egg problem since the TOC is only available after parsing, but we want to add more to the TOC based on the output of other modules. The solution to this scenario is to enable executing the parse and render phase individually. This will allow us to gather the TOC earlier in the application, combine it with other TOC elements, and then later render the output using this updated TOC. --- lib/Builder.php | 59 +++++++++++++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 26 deletions(-) diff --git a/lib/Builder.php b/lib/Builder.php index 1be06378..37864c8d 100644 --- a/lib/Builder.php +++ b/lib/Builder.php @@ -138,27 +138,8 @@ public function build( string $directory, string $targetDirectory = 'output' ): void { - // Creating output directory if doesn't exists - if (! is_dir($targetDirectory)) { - $this->filesystem->mkdir($targetDirectory, 0755); - } - - $indexFilename = sprintf('%s.%s', $this->indexName, $this->configuration->getSourceFileExtension()); - if (! file_exists($directory . '/' . $indexFilename)) { - throw new InvalidArgumentException(sprintf('Could not find index file "%s" in "%s"', $indexFilename, $directory)); - } - - if ($this->configuration->getUseCachedMetas()) { - $this->cachedMetasLoader->loadCachedMetaEntries($targetDirectory, $this->metas); - } - - $parseQueue = $this->scan($directory, $targetDirectory); - - $this->parse($directory, $targetDirectory, $parseQueue); - + $this->parse($directory, $targetDirectory); $this->render($directory, $targetDirectory); - - $this->cachedMetasLoader->cacheMetaEntries($targetDirectory, $this->metas); } public function copy(string $source, ?string $destination = null): self @@ -200,7 +181,31 @@ private function scan(string $directory, string $targetDirectory): ParseQueue return $scanner->scan(); } - private function parse(string $directory, string $targetDirectory, ParseQueue $parseQueue): void + public function parse(string $directory, string $targetDirectory) : void + { + $this->ensureDirectoryExists($targetDirectory); + + $indexFilename = sprintf('%s.%s', $this->indexName, $this->configuration->getSourceFileExtension()); + if (! file_exists($directory . '/' . $indexFilename)) { + throw new InvalidArgumentException(sprintf('Could not find index file "%s" in "%s"', $indexFilename, $directory)); + } + + if ($this->configuration->getUseCachedMetas()) { + $this->cachedMetasLoader->loadCachedMetaEntries($targetDirectory, $this->metas); + } + + $parseQueue = $this->scan($directory, $targetDirectory); + + $this->doParse($directory, $targetDirectory, $parseQueue); + + if (! $this->configuration->getUseCachedMetas()) { + return; + } + + $this->cachedMetasLoader->cacheMetaEntries($targetDirectory, $this->metas); + } + + private function doParse(string $directory, string $targetDirectory, ParseQueue $parseQueue): void { $this->configuration->dispatchEvent( PreBuildParseEvent::PRE_BUILD_PARSE, @@ -220,8 +225,10 @@ private function parse(string $directory, string $targetDirectory, ParseQueue $p $parseQueueProcessor->process($parseQueue); } - private function render(string $directory, string $targetDirectory): void + public function render(string $directory, string $targetDirectory): void { + $this->ensureDirectoryExists($targetDirectory); + $this->configuration->dispatchEvent( PreBuildRenderEvent::PRE_BUILD_RENDER, new PreBuildRenderEvent($this, $directory, $targetDirectory) @@ -238,12 +245,12 @@ private function render(string $directory, string $targetDirectory): void ); } - private function getScannerFinder(): Finder + private function ensureDirectoryExists(string $targetDirectory): void { - if ($this->scannerFinder === null) { - $this->scannerFinder = new Finder(); + if (is_dir($targetDirectory)) { + return; } - return $this->scannerFinder; + $this->filesystem->mkdir($targetDirectory, 0755); } } From b82dd67bae17dbfd7842177e9f063015bbb1c36f Mon Sep 17 00:00:00 2001 From: Mike van Riel Date: Sun, 26 Jan 2020 17:31:42 +0100 Subject: [PATCH 2/2] Persist meta's as cache after rendering In my previous commit I believed that the meta's could be cached after parsing had completed but the unit tests quickly proved me wrong. During the rendering phase will the dependencies be resolved and persisted in the meta's collection. This means that when we try to persist the cache too soon, that this information is lost. It might be worth investigating if there is a way to resolve this dependencies before rendering; but that is beyond scope of this change. --- lib/Builder.php | 30 +++++++++++++++++++++++------- tests/Builder/BuilderTest.php | 9 +++++++++ 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/lib/Builder.php b/lib/Builder.php index 37864c8d..a88c4d2b 100644 --- a/lib/Builder.php +++ b/lib/Builder.php @@ -181,7 +181,7 @@ private function scan(string $directory, string $targetDirectory): ParseQueue return $scanner->scan(); } - public function parse(string $directory, string $targetDirectory) : void + public function parse(string $directory, string $targetDirectory): void { $this->ensureDirectoryExists($targetDirectory); @@ -197,12 +197,6 @@ public function parse(string $directory, string $targetDirectory) : void $parseQueue = $this->scan($directory, $targetDirectory); $this->doParse($directory, $targetDirectory, $parseQueue); - - if (! $this->configuration->getUseCachedMetas()) { - return; - } - - $this->cachedMetasLoader->cacheMetaEntries($targetDirectory, $this->metas); } private function doParse(string $directory, string $targetDirectory, ParseQueue $parseQueue): void @@ -243,6 +237,8 @@ public function render(string $directory, string $targetDirectory): void PostBuildRenderEvent::POST_BUILD_RENDER, new PostBuildRenderEvent($this, $directory, $targetDirectory) ); + + $this->storeCache($targetDirectory); } private function ensureDirectoryExists(string $targetDirectory): void @@ -253,4 +249,24 @@ private function ensureDirectoryExists(string $targetDirectory): void $this->filesystem->mkdir($targetDirectory, 0755); } + + /** + * Persist the cache to disk. + * + * The meta's are cached after we finished rendering as the renderer resolves the filenames of the dependencies in + * these meta's. + */ + private function storeCache(string $targetDirectory): void + { + $this->cachedMetasLoader->cacheMetaEntries($targetDirectory, $this->metas); + } + + private function getScannerFinder(): Finder + { + if ($this->scannerFinder === null) { + $this->scannerFinder = new Finder(); + } + + return $this->scannerFinder; + } } diff --git a/tests/Builder/BuilderTest.php b/tests/Builder/BuilderTest.php index abcfb7f8..1938ae4f 100644 --- a/tests/Builder/BuilderTest.php +++ b/tests/Builder/BuilderTest.php @@ -7,6 +7,7 @@ use Doctrine\RST\Builder; use Doctrine\RST\Meta\MetaEntry; use Doctrine\Tests\RST\BaseBuilderTest; +use InvalidArgumentException; use function array_unique; use function array_values; @@ -339,6 +340,14 @@ public function testReferenceToTitleWith2CharactersLong(): void ); } + public function testItThrowsWhenIndexFileCannotBeFound(): void + { + $builder = new Builder(); + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Could not find index file "index.rst" in "/tmp"'); + $builder->parse('/tmp', '/tmp/test'); + } + protected function getFixturesDirectory(): string { return 'Builder';