diff --git a/system/src/Grav/Common/Helpers/YamlLinter.php b/system/src/Grav/Common/Helpers/YamlLinter.php index 64de4c493..7136ae06c 100644 --- a/system/src/Grav/Common/Helpers/YamlLinter.php +++ b/system/src/Grav/Common/Helpers/YamlLinter.php @@ -27,39 +27,47 @@ class YamlLinter { /** * @param string|null $folder + * @param callable|null $callback Optional callback for progress: function(string $file, bool $success, ?string $error) * @return array */ - public static function lint(?string $folder = null) + public static function lint(?string $folder = null, ?callable $callback = null) { if (null !== $folder) { $folder = $folder ?: GRAV_ROOT; - return static::recurseFolder($folder); + return static::recurseFolder($folder, '(md|yaml)', $callback); } - return array_merge(static::lintConfig(), static::lintPages(), static::lintBlueprints()); + return array_merge( + static::lintConfig($callback), + static::lintPages($callback), + static::lintBlueprints($callback) + ); } /** + * @param callable|null $callback Optional callback for progress: function(string $file, bool $success, ?string $error) * @return array */ - public static function lintPages() + public static function lintPages(?callable $callback = null) { - return static::recurseFolder('page://'); + return static::recurseFolder('page://', '(md|yaml)', $callback); } /** + * @param callable|null $callback Optional callback for progress: function(string $file, bool $success, ?string $error) * @return array */ - public static function lintConfig() + public static function lintConfig(?callable $callback = null) { - return static::recurseFolder('config://'); + return static::recurseFolder('config://', '(md|yaml)', $callback); } /** + * @param callable|null $callback Optional callback for progress: function(string $file, bool $success, ?string $error) * @return array */ - public static function lintBlueprints() + public static function lintBlueprints(?callable $callback = null) { /** @var UniformResourceLocator $locator */ $locator = Grav::instance()['locator']; @@ -68,15 +76,16 @@ class YamlLinter $theme_path = 'themes://' . $current_theme . '/blueprints'; $locator->addPath('blueprints', '', [$theme_path]); - return static::recurseFolder('blueprints://'); + return static::recurseFolder('blueprints://', '(md|yaml)', $callback); } /** * @param string $path * @param string $extensions + * @param callable|null $callback Optional callback for progress: function(string $file, bool $success, ?string $error) * @return array */ - public static function recurseFolder($path, $extensions = '(md|yaml)') + public static function recurseFolder($path, $extensions = '(md|yaml)', ?callable $callback = null) { $lint_errors = []; @@ -93,10 +102,17 @@ class YamlLinter /** @var RecursiveDirectoryIterator $file */ foreach ($iterator as $filepath => $file) { + $relativePath = str_replace(GRAV_ROOT, '', $filepath); try { Yaml::parse(static::extractYaml($filepath)); + if ($callback) { + $callback($relativePath, true, null); + } } catch (Exception $e) { - $lint_errors[str_replace(GRAV_ROOT, '', $filepath)] = $e->getMessage(); + $lint_errors[$relativePath] = $e->getMessage(); + if ($callback) { + $callback($relativePath, false, $e->getMessage()); + } } } diff --git a/system/src/Grav/Console/Cli/YamlLinterCommand.php b/system/src/Grav/Console/Cli/YamlLinterCommand.php index c794d8487..94be94305 100644 --- a/system/src/Grav/Console/Cli/YamlLinterCommand.php +++ b/system/src/Grav/Console/Cli/YamlLinterCommand.php @@ -53,62 +53,88 @@ class YamlLinterCommand extends GravCommand $io->title('Yaml Linter'); + $verbose = $io->isVerbose(); + $checked = 0; + $callback = $verbose ? function (string $file, bool $success, ?string $error) use ($io, &$checked) { + $checked++; + if ($success) { + $io->writeln("[OK] {$file}"); + } else { + $io->writeln("[ERROR] {$file}"); + } + } : null; + $error = 0; if ($input->getOption('all')) { $io->section('All'); - $errors = YamlLinter::lint(''); + $errors = YamlLinter::lint('', $callback); - if (empty($errors)) { - $io->success('No YAML Linting issues found'); - } else { + $this->displayResult($errors, $io, 'No YAML Linting issues found', $verbose, $checked); + if (!empty($errors)) { $error = 1; - $this->displayErrors($errors, $io); } } elseif ($folder = $input->getOption('folder')) { $io->section($folder); - $errors = YamlLinter::lint($folder); + $errors = YamlLinter::lint($folder, $callback); - if (empty($errors)) { - $io->success('No YAML Linting issues found'); - } else { + $this->displayResult($errors, $io, 'No YAML Linting issues found', $verbose, $checked); + if (!empty($errors)) { $error = 1; - $this->displayErrors($errors, $io); } } else { $io->section('User Configuration'); - $errors = YamlLinter::lintConfig(); + $checked = 0; + $errors = YamlLinter::lintConfig($callback); - if (empty($errors)) { - $io->success('No YAML Linting issues with configuration'); - } else { + $this->displayResult($errors, $io, 'No YAML Linting issues with configuration', $verbose, $checked); + if (!empty($errors)) { $error = 1; - $this->displayErrors($errors, $io); } $io->section('Pages Frontmatter'); - $errors = YamlLinter::lintPages(); + $checked = 0; + $errors = YamlLinter::lintPages($callback); - if (empty($errors)) { - $io->success('No YAML Linting issues with pages'); - } else { + $this->displayResult($errors, $io, 'No YAML Linting issues with pages', $verbose, $checked); + if (!empty($errors)) { $error = 1; - $this->displayErrors($errors, $io); } $io->section('Page Blueprints'); - $errors = YamlLinter::lintBlueprints(); + $checked = 0; + $errors = YamlLinter::lintBlueprints($callback); - if (empty($errors)) { - $io->success('No YAML Linting issues with blueprints'); - } else { + $this->displayResult($errors, $io, 'No YAML Linting issues with blueprints', $verbose, $checked); + if (!empty($errors)) { $error = 1; - $this->displayErrors($errors, $io); } } return $error; } + /** + * @param array $errors + * @param SymfonyStyle $io + * @param string $successMessage + * @param bool $verbose + * @param int $checked + * @return void + */ + protected function displayResult(array $errors, SymfonyStyle $io, string $successMessage, bool $verbose, int $checked): void + { + if ($verbose) { + $io->newLine(); + $io->writeln("Files checked: {$checked}"); + } + + if (empty($errors)) { + $io->success($successMessage); + } else { + $this->displayErrors($errors, $io); + } + } + /** * @param array $errors * @param SymfonyStyle $io