Skip to content

Commit b945a54

Browse files
committed
feature #71 Created a doc builder, doc config and doc result classes (javiereguiluz)
This PR was squashed before being merged into the master branch. Discussion ---------- Created a doc builder, doc config and doc result classes This parser was originally created with this target -> "integrating it in symofny.com should require the absolute minimum number of changes". That's where the PHAR and command made a lot of sense. However, circumstances have changed completely. Now the target is "to create the best possible PHP-based RST parser and doc builder". That means less maintenance, less things to set up, less concepts to learn etc. That's why I created this PR. The idea is that parsing RST contents would mean to run `composer require <this-package>` and then do this in your own code: ```php $buildResult = $docBuilder->build($buildConfig); ``` Comments: * The `BuildResult` class is a very simple value object to abstract all the build error messages. * The `DocBuilder` is completely new ... but it's based on the command previously used to build docs. * `BuildConfig` is a rename + simplification of the previous `BuildContext`. I think the new name is more precise, because this object mostly/only stores config values and no state/context. * I also added some new options to `BuildConfig` which we need in `CopyImagesListener`. I know this looks like a lot of changes ... but I consider it just a simplification of the current code. I thank you all for the previous work which provided a solid foundation. Lastly, I've been using this for weeks to build bundles docs in symfony.com with success, so I consider this tested enough 🤞 Commits ------- 9574a5a Restore code 8018e38 Create dirs automatically if they don't exist c88f4a4 - 53e5996 Fixed tests 73830b8 Created a doc builder, doc config and doc result classes 1480a16 Introduce a DocBuilder and BuildResult
2 parents 14300dc + 9574a5a commit b945a54

16 files changed

+443
-314
lines changed

composer.lock

Lines changed: 10 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Application.php

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,23 +17,12 @@
1717
class Application
1818
{
1919
private $application;
20-
private $buildContext;
20+
private $buildConfig;
2121

2222
public function __construct(string $symfonyVersion)
2323
{
2424
$this->application = new BaseApplication();
25-
26-
$configuration = [
27-
'symfony_api_url' => 'https://api.symfony.com/%s',
28-
'php_doc_url' => 'https://secure.php.net/manual/en',
29-
'symfony_doc_url' => 'https://symfony.com/doc/%s',
30-
];
31-
$this->buildContext = new BuildContext(
32-
$symfonyVersion,
33-
sprintf($configuration['symfony_api_url'], $symfonyVersion),
34-
$configuration['php_doc_url'],
35-
sprintf($configuration['symfony_doc_url'], $symfonyVersion)
36-
);
25+
$this->buildConfig = new BuildConfig();
3726
}
3827

3928
public function run(InputInterface $input): int
@@ -46,7 +35,7 @@ public function run(InputInterface $input): int
4635
false === getenv('SYMFONY_VERSION') ? 'master' : getenv('SYMFONY_VERSION')
4736
);
4837
$this->application->getDefinition()->addOption($inputOption);
49-
$this->application->add(new BuildDocsCommand($this->buildContext));
38+
$this->application->add(new BuildDocsCommand($this->buildConfig));
5039

5140
return $this->application->run($input);
5241
}

src/BuildConfig.php

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Docs Builder package.
5+
* (c) Ryan Weaver <ryan@symfonycasts.com>
6+
* For the full copyright and license information, please view the LICENSE
7+
* file that was distributed with this source code.
8+
*/
9+
10+
namespace SymfonyDocsBuilder;
11+
12+
use Doctrine\RST\Configuration;
13+
use Symfony\Component\Filesystem\Filesystem;
14+
use Symfony\Component\Finder\Finder;
15+
16+
class BuildConfig
17+
{
18+
private const PHP_DOC_URL = 'https://secure.php.net/manual/en';
19+
private const SYMFONY_API_URL = 'https://api.symfony.com/{symfonyVersion}';
20+
private const SYMFONY_DOC_URL = 'https://symfony.com/doc/{symfonyVersion}';
21+
22+
private $useBuildCache;
23+
private $theme;
24+
private $symfonyVersion;
25+
private $contentDir;
26+
private $outputDir;
27+
private $cacheDir;
28+
private $imagesDir;
29+
private $imagesPublicPrefix;
30+
private $subdirectoryToBuild;
31+
private $excludedPaths;
32+
private $fileFinder;
33+
34+
public function __construct()
35+
{
36+
$this->useBuildCache = true;
37+
$this->theme = Configuration::THEME_DEFAULT;
38+
$this->symfonyVersion = '4.4';
39+
$this->excludedPaths = [];
40+
$this->imagesPublicPrefix = '/_images';
41+
}
42+
43+
public function createFileFinder(): Finder
44+
{
45+
if (null === $this->fileFinder) {
46+
$this->fileFinder = new Finder();
47+
$this->fileFinder
48+
->in($this->getContentDir())
49+
// TODO - read this from the rst-parser Configuration
50+
->name('*.rst')
51+
->notName('*.rst.inc')
52+
->files()
53+
->exclude($this->excludedPaths);
54+
}
55+
56+
// clone to get a fresh instance and not share state
57+
return clone $this->fileFinder;
58+
}
59+
60+
public function getTheme(): string
61+
{
62+
return $this->theme;
63+
}
64+
65+
public function getSymfonyVersion(): string
66+
{
67+
return $this->symfonyVersion;
68+
}
69+
70+
public function getPhpDocUrl(): string
71+
{
72+
return self::PHP_DOC_URL;
73+
}
74+
75+
public function getSymfonyApiUrl(): string
76+
{
77+
return str_replace('{symfonyVersion}', $this->getSymfonyVersion(), self::SYMFONY_API_URL);
78+
}
79+
80+
public function getSymfonyDocUrl(): string
81+
{
82+
return str_replace('{symfonyVersion}', $this->getSymfonyVersion(), self::SYMFONY_DOC_URL);
83+
}
84+
85+
public function disableBuildCache(): self
86+
{
87+
$this->useBuildCache = false;
88+
89+
return $this;
90+
}
91+
92+
public function isBuildCacheEnabled(): bool
93+
{
94+
return $this->useBuildCache;
95+
}
96+
97+
public function getContentDir(): string
98+
{
99+
if (null === $this->contentDir) {
100+
throw new \InvalidArgumentException('RST contents directory is not defined. Set it with the setContentDir() method.');
101+
}
102+
103+
return $this->contentDir;
104+
}
105+
106+
public function getSubdirectoryToBuild(): ?string
107+
{
108+
return $this->subdirectoryToBuild;
109+
}
110+
111+
public function getOutputDir(): string
112+
{
113+
return $this->outputDir ?: $this->getContentDir().'/output';
114+
}
115+
116+
public function getCacheDir(): string
117+
{
118+
return $this->cacheDir ?: $this->getOutputDir().'/.cache';
119+
}
120+
121+
public function getImagesDir(): string
122+
{
123+
return $this->imagesDir ?: $this->getOutputDir().'/_images';
124+
}
125+
126+
public function getImagesPublicPrefix(): string
127+
{
128+
return $this->imagesPublicPrefix;
129+
}
130+
131+
public function setSymfonyVersion(string $version): self
132+
{
133+
$this->symfonyVersion = $version;
134+
135+
return $this;
136+
}
137+
138+
public function setTheme(string $theme): self
139+
{
140+
$this->theme = $theme;
141+
142+
return $this;
143+
}
144+
145+
public function setContentDir(string $contentDir): self
146+
{
147+
if (!file_exists($contentDir)) {
148+
throw new \InvalidArgumentException(sprintf('RST contents directory "%s" does not exist', $contentDir));
149+
}
150+
151+
$this->contentDir = rtrim(realpath($contentDir), DIRECTORY_SEPARATOR);
152+
153+
return $this;
154+
}
155+
156+
public function setSubdirectoryToBuild(string $contentSubDir): self
157+
{
158+
$this->subdirectoryToBuild = trim($contentSubDir, DIRECTORY_SEPARATOR);
159+
160+
return $this;
161+
}
162+
163+
public function setOutputDir(string $outputDir): self
164+
{
165+
(new Filesystem())->mkdir($outputDir);
166+
if (!file_exists($outputDir)) {
167+
throw new \InvalidArgumentException(sprintf('Doc builder output directory "%s" does not exist', $outputDir));
168+
}
169+
170+
$this->outputDir = rtrim(realpath($outputDir), DIRECTORY_SEPARATOR);
171+
172+
return $this;
173+
}
174+
175+
public function setCacheDir(string $cacheDir): self
176+
{
177+
(new Filesystem())->mkdir($cacheDir);
178+
if (!file_exists($cacheDir)) {
179+
throw new \InvalidArgumentException(sprintf('Doc builder cache directory "%s" does not exist', $cacheDir));
180+
}
181+
182+
$this->cacheDir = rtrim(realpath($cacheDir), DIRECTORY_SEPARATOR);
183+
184+
return $this;
185+
}
186+
187+
/**
188+
* The directory where the images will be copied to. E.g. use this to
189+
* copy them into the public/ directory of a Symfony application.
190+
*/
191+
public function setImagesDir(string $imagesDir): self
192+
{
193+
(new Filesystem())->mkdir($imagesDir);
194+
if (!file_exists($imagesDir)) {
195+
throw new \InvalidArgumentException(sprintf('Doc builder images directory "%s" does not exist', $imagesDir));
196+
}
197+
198+
$this->imagesDir = rtrim(realpath($imagesDir), DIRECTORY_SEPARATOR);
199+
200+
return $this;
201+
}
202+
203+
/**
204+
* The string prefixes to the `src` attribute of `<img>` tags. This is useful when
205+
* publishing images in a public directory of a Symfony application.
206+
*/
207+
public function setImagesPublicPrefix(string $prefix): self
208+
{
209+
$this->imagesPublicPrefix = $prefix;
210+
211+
return $this;
212+
}
213+
214+
public function setExcludedPaths(array $excludedPaths)
215+
{
216+
if (null !== $this->fileFinder) {
217+
throw new \LogicException('setExcludePaths() cannot be called after getFileFinder() (because the Finder has been initialized).');
218+
}
219+
220+
$this->excludedPaths = $excludedPaths;
221+
}
222+
}

0 commit comments

Comments
 (0)