Explorar o código

Support multiple docs

shalvah %!s(int64=3) %!d(string=hai) anos
pai
achega
cc6c95eed2

+ 17 - 0
camel/Camel.php

@@ -19,9 +19,26 @@ class Camel
      * @var array<string, string>
      */
     public static array $groupFileNames = [];
+
+    /**
+     * @deprecated Use the cacheDir() method instead
+     */
     public static string $cacheDir = ".scribe/endpoints.cache";
+    /**
+     * @deprecated Use the camelDir() method instead
+     */
     public static string $camelDir = ".scribe/endpoints";
 
+    public static function cacheDir(string $docsName = 'scribe')
+    {
+        return ".$docsName/endpoints.cache";
+    }
+
+    public static function camelDir(string $docsName = 'scribe')
+    {
+        return ".$docsName/endpoints";
+    }
+
     /**
      * Load endpoints from the Camel files into groups (arrays).
      *

+ 36 - 14
src/Commands/GenerateDocumentation.php

@@ -5,6 +5,7 @@ namespace Knuckles\Scribe\Commands;
 use Illuminate\Console\Command;
 use Illuminate\Support\Arr;
 use Illuminate\Support\Facades\URL;
+use Illuminate\Support\Str;
 use Knuckles\Camel\Camel;
 use Knuckles\Camel\Output\OutputEndpointData;
 use Knuckles\Scribe\Exceptions\GroupNotFound;
@@ -23,7 +24,7 @@ class GenerateDocumentation extends Command
                             {--force : Discard any changes you've made to the YAML or Markdown files}
                             {--no-extraction : Skip extraction of route and API info and just transform the YAML and Markdown files into HTML}
                             {--no-upgrade-check : Skip checking for config file upgrades. Won't make things faster, but can be helpful if the command is buggy}
-                            {--config= : choose which config file to use}
+                            {--config=scribe : choose which config file to use}
     ";
 
     protected $description = 'Generate API documentation from your Laravel/Dingo routes.';
@@ -34,6 +35,8 @@ class GenerateDocumentation extends Command
 
     private bool $forcing;
 
+    protected string $configName;
+
     public function newLine($count = 1)
     {
         // TODO Remove when Laravel 6 is no longer supported
@@ -45,9 +48,9 @@ class GenerateDocumentation extends Command
     {
         $this->bootstrap();
 
-        $groupedEndpointsInstance = $groupedEndpointsFactory->make($this, $routeMatcher);
+        $groupedEndpointsInstance = $groupedEndpointsFactory->make($this, $routeMatcher, $this->configName);
 
-        $userDefinedEndpoints = Camel::loadUserDefinedEndpoints(Camel::$camelDir);
+        $userDefinedEndpoints = Camel::loadUserDefinedEndpoints(Camel::camelDir($this->configName));
         $groupedEndpoints = $this->mergeUserDefinedEndpoints(
             $groupedEndpointsInstance->get(),
             $userDefinedEndpoints
@@ -58,7 +61,7 @@ class GenerateDocumentation extends Command
             $this->writeExampleCustomEndpoint();
         }
 
-        $writer = new Writer($this->docConfig);
+        $writer = new Writer($this->docConfig, $this->configName);
         $writer->writeDocs($groupedEndpoints);
 
         if ($groupedEndpointsInstance->hasEncounteredErrors()) {
@@ -67,6 +70,8 @@ class GenerateDocumentation extends Command
         }
 
         $this->upgradeConfigFileIfNeeded();
+
+        $this->sayGoodbye();
     }
 
     public function isForcing(): bool
@@ -91,16 +96,17 @@ class GenerateDocumentation extends Command
 
         c::bootstrapOutput($this->output);
 
-        $this->docConfig = new DocumentationConfig(config('scribe'));
-
-        if($this->option('config')){
-            $config = config_path($this->option('config')).".php";
-            if(!file_exists($config)){
-                die("There is no suitable config found at {$config}\n");
+        $this->configName = $this->option('config');
+        if ($this->configName !== 'scribe') {
+            $configPath = config_path($this->configName) . ".php";
+            if (!file_exists($configPath)) {
+                c::error("The specified config file doesn't exist: {$configPath}.\n");
+                exit(1);
             }
-            $this->docConfig = new DocumentationConfig(config($this->option('config')));
         }
 
+        $this->docConfig = new DocumentationConfig(config($this->configName));
+
         // Force root URL so it works in Postman collection
         $baseUrl = $this->docConfig->get('base_url') ?? config('app.url');
         URL::forceRootUrl($baseUrl);
@@ -109,7 +115,8 @@ class GenerateDocumentation extends Command
         $this->shouldExtract = !$this->option('no-extraction');
 
         if ($this->forcing && !$this->shouldExtract) {
-            throw new \Exception("Can't use --force and --no-extraction together.");
+            c::error("Can't use --force and --no-extraction together.\n");
+            exit(1);
         }
 
         // Reset this map (useful for tests)
@@ -180,7 +187,7 @@ class GenerateDocumentation extends Command
     protected function writeExampleCustomEndpoint(): void
     {
         // We add an example to guide users in case they need to add a custom endpoint.
-        copy(__DIR__ . '/../../resources/example_custom_endpoint.yaml', Camel::$camelDir . '/custom.0.yaml');
+        copy(__DIR__ . '/../../resources/example_custom_endpoint.yaml', Camel::camelDir($this->configName) . '/custom.0.yaml');
     }
 
     protected function upgradeConfigFileIfNeeded(): void
@@ -189,7 +196,7 @@ class GenerateDocumentation extends Command
 
         $this->info("Checking for any pending upgrades to your config file...");
         try {
-            $upgrader = Upgrader::ofConfigFile('config/scribe.php', __DIR__ . '/../../config/scribe.php')
+            $upgrader = Upgrader::ofConfigFile("config/{$this->configName}.php", __DIR__ . '/../../config/scribe.php')
                 ->dontTouch(
                     'routes', 'example_languages', 'database_connections_to_transact', 'strategies', 'laravel.middleware',
                     'postman.overrides', 'openapi.overrides'
@@ -222,4 +229,19 @@ class GenerateDocumentation extends Command
         }
 
     }
+
+    protected function sayGoodbye(): void
+    {
+        $message = 'All done. ';
+        if ($this->docConfig->get('type') == 'laravel') {
+            if ($this->docConfig->get('laravel.add_routes')) {
+                $message .= 'Visit your docs at ' . url($this->docConfig->get('laravel.docs_url'));
+            }
+        } else if (Str::endsWith(public_path(), 'public') && Str::startsWith($this->docConfig->get('static.output_path'), 'public/')) {
+            $message = 'Visit your docs at ' . url(str_replace('public/', '', $this->docConfig->get('static.output_path')));
+        }
+
+        $this->newLine();
+        c::success($message);
+    }
 }

+ 4 - 3
src/Extracting/ApiDetails.php

@@ -17,16 +17,17 @@ class ApiDetails
 
     private bool $preserveUserChanges;
 
-    private string $markdownOutputPath = '.scribe';
+    private string $markdownOutputPath;
 
     private string $fileHashesTrackingFile;
 
     private array $lastKnownFileContentHashes = [];
 
-    public function __construct(DocumentationConfig $config = null, bool $preserveUserChanges = true)
+    public function __construct(DocumentationConfig $config = null, bool $preserveUserChanges = true, string $docsName = 'scribe')
     {
+        $this->markdownOutputPath = ".{$docsName}"; //.scribe by default
         // If no config is injected, pull from global. Makes testing easier.
-        $this->config = $config ?: new DocumentationConfig(config('scribe'));
+        $this->config = $config ?: new DocumentationConfig(config($docsName));
         $this->baseUrl = $this->config->get('base_url') ?? config('app.url');
         $this->preserveUserChanges = $preserveUserChanges;
 

+ 9 - 8
src/GroupedEndpoints/GroupedEndpointsFactory.php

@@ -8,29 +8,30 @@ use Knuckles\Scribe\Matching\RouteMatcherInterface;
 
 class GroupedEndpointsFactory
 {
-    public function make(GenerateDocumentation $command, RouteMatcherInterface $routeMatcher): GroupedEndpointsContract
+    public function make(GenerateDocumentation $command, RouteMatcherInterface $routeMatcher, string $docsName = 'scribe'): GroupedEndpointsContract
     {
         if ($command->isForcing()) {
-            return $this->makeGroupedEndpointsFromApp($command, $routeMatcher, false);
+            return $this->makeGroupedEndpointsFromApp($command, $routeMatcher, false, $docsName);
         }
 
         if ($command->shouldExtract()) {
-            return $this->makeGroupedEndpointsFromApp($command, $routeMatcher, true);
+            return $this->makeGroupedEndpointsFromApp($command, $routeMatcher, true, $docsName);
         }
 
-        return $this->makeGroupedEndpointsFromCamelDir();
+        return $this->makeGroupedEndpointsFromCamelDir($docsName);
     }
 
     protected function makeGroupedEndpointsFromApp(
         GenerateDocumentation $command,
         RouteMatcherInterface $routeMatcher,
-        bool $preserveUserChanges
+        bool $preserveUserChanges,
+        string $docsName = 'scribe'
     ): GroupedEndpointsFromApp {
-        return new GroupedEndpointsFromApp($command, $routeMatcher, $preserveUserChanges);
+        return new GroupedEndpointsFromApp($command, $routeMatcher, $preserveUserChanges, $docsName);
     }
 
-    protected function makeGroupedEndpointsFromCamelDir(): GroupedEndpointsFromCamelDir
+    protected function makeGroupedEndpointsFromCamelDir(string $docsName = 'scribe'): GroupedEndpointsFromCamelDir
     {
-        return new GroupedEndpointsFromCamelDir();
+        return new GroupedEndpointsFromCamelDir($docsName);
     }
 }

+ 9 - 4
src/GroupedEndpoints/GroupedEndpointsFromApp.php

@@ -24,6 +24,7 @@ use Symfony\Component\Yaml\Yaml;
 
 class GroupedEndpointsFromApp implements GroupedEndpointsContract
 {
+    protected string $docsName;
     private GenerateDocumentation $command;
     private RouteMatcherInterface $routeMatcher;
     private DocumentationConfig $docConfig;
@@ -35,15 +36,19 @@ class GroupedEndpointsFromApp implements GroupedEndpointsContract
 
     private array $endpointGroupIndexes = [];
 
-    public function __construct(GenerateDocumentation $command, RouteMatcherInterface $routeMatcher, $preserveUserChanges)
+    public function __construct(
+        GenerateDocumentation $command, RouteMatcherInterface $routeMatcher,
+        bool $preserveUserChanges, string $docsName = 'scribe'
+    )
     {
         $this->command = $command;
         $this->routeMatcher = $routeMatcher;
         $this->docConfig = $command->getDocConfig();
         $this->preserveUserChanges = $preserveUserChanges;
+        $this->docsName = $docsName;
 
-        static::$camelDir = Camel::$camelDir;
-        static::$cacheDir = Camel::$cacheDir;
+        static::$camelDir = Camel::camelDir($this->docsName);
+        static::$cacheDir = Camel::cacheDir($this->docsName);
     }
 
     public function get(): array
@@ -282,7 +287,7 @@ class GroupedEndpointsFromApp implements GroupedEndpointsContract
 
     protected function makeApiDetails(): ApiDetails
     {
-        return new ApiDetails($this->docConfig, !$this->command->option('force'));
+        return new ApiDetails($this->docConfig, !$this->command->option('force'), $this->docsName);
     }
 
     /**

+ 10 - 3
src/GroupedEndpoints/GroupedEndpointsFromCamelDir.php

@@ -7,15 +7,22 @@ use Knuckles\Scribe\Commands\GenerateDocumentation;
 
 class GroupedEndpointsFromCamelDir implements GroupedEndpointsContract
 {
+    protected string $docsName;
+
+    public function __construct(string $docsName = 'scribe')
+    {
+        $this->docsName = $docsName;
+    }
+
     public function get(): array
     {
-        if (!is_dir(Camel::$camelDir)) {
+        if (!is_dir(Camel::camelDir($this->docsName))) {
             throw new \InvalidArgumentException(
-                "Can't use --no-extraction because there are no endpoints in the " . Camel::$camelDir . " directory."
+                "Can't use --no-extraction because there are no endpoints in the " . Camel::camelDir($this->docsName) . " directory."
             );
         }
 
-        return Camel::loadEndpointsIntoGroups(Camel::$camelDir);
+        return Camel::loadEndpointsIntoGroups(Camel::camelDir($this->docsName));
     }
 
     public function hasEncounteredErrors(): bool

+ 8 - 7
src/Writing/Writer.php

@@ -15,7 +15,7 @@ class Writer
 
     private bool $isStatic;
 
-    private string $markdownOutputPath = '.scribe';
+    private string $markdownOutputPath;
 
     private string $staticTypeOutputPath;
 
@@ -29,15 +29,16 @@ class Writer
             'js' => null,
             'css' => null,
             'images' => null,
-        ]
+        ],
     ];
 
     private string $laravelAssetsPath;
 
-    public function __construct(DocumentationConfig $config = null)
+    public function __construct(DocumentationConfig $config = null, $docsName = 'scribe')
     {
+        $this->markdownOutputPath = ".{$docsName}"; //.scribe by default
         // If no config is injected, pull from global. Makes testing easier.
-        $this->config = $config ?: new DocumentationConfig(config('scribe'));
+        $this->config = $config ?: new DocumentationConfig(config($docsName));
 
         $this->isStatic = $this->config->get('type') === 'static';
         $this->staticTypeOutputPath = rtrim($this->config->get('static.output_path', 'public/docs'), '/');
@@ -178,7 +179,7 @@ class Writer
 
     public function writeHtmlDocs(array $groupedEndpoints): void
     {
-        c::info('Writing HTML docs...');
+        c::info('Writing ' . ($this->isStatic ? 'HTML' : 'Blade') . ' docs...');
 
         // Then we convert them to HTML, and throw in the endpoints as well.
         /** @var HtmlWriter $writer */
@@ -191,14 +192,14 @@ class Writer
 
         if ($this->isStatic) {
             $outputPath = rtrim($this->staticTypeOutputPath, '/') . '/';
-            c::success("Wrote HTML docs to: $outputPath");
+            c::success("Wrote HTML docs and assets to: $outputPath");
             $this->generatedFiles['html'] = realpath("{$outputPath}index.html");
             $assetsOutputPath = $outputPath;
         } else {
             $outputPath = rtrim($this->laravelTypeOutputPath, '/') . '/';
             c::success("Wrote Blade docs to: $outputPath");
             $this->generatedFiles['blade'] = realpath("{$outputPath}index.blade.php");
-            $assetsOutputPath = app()->get('path.public').$this->laravelAssetsPath;
+            $assetsOutputPath = app()->get('path.public') . $this->laravelAssetsPath;
             c::success("Wrote Laravel assets to: " . realpath($assetsOutputPath));
         }
         $this->generatedFiles['assets']['js'] = realpath("{$assetsOutputPath}js");