Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
15 changes: 11 additions & 4 deletions src/Console/Command/Hyva/CompatibilityCheckCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

/**
* Command to check Magento modules for Hyvä theme compatibility issues
*
* Scans modules for RequireJS, Knockout.js, jQuery, and UI Components usage
* that would be incompatible with Hyvä themes.
*/
class CompatibilityCheckCommand extends AbstractCommand
{
private const OPTION_SHOW_ALL = 'show-all';
Expand Down Expand Up @@ -169,10 +175,11 @@ private function runScan(
OutputInterface $output
): int {

// Determine filter logic:
// - Default (no flags): Scan third-party only (exclude Magento_* but include vendor third-party)
// - With --include-vendor: Scan everything including Magento_*
// - With --third-party-only: Explicitly scan only third-party
// Determine filter logic for vendor and third-party modules:
// - excludeVendor controls whether to scan modules in the vendor/ directory
// - By default (no flags): scan third-party modules including those in vendor/
// - With --include-vendor: scan everything including Magento_* core modules
// - With --third-party-only: explicitly scan only third-party modules
$scanThirdPartyOnly = $thirdPartyOnly || (!$includeVendor && !$thirdPartyOnly);
$excludeVendor = false; // Always include vendor for third-party scanning

Expand Down
30 changes: 27 additions & 3 deletions src/Service/Hyva/CompatibilityChecker.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

/**
* Service that orchestrates Hyvä compatibility checking across Magento modules
*
* This service scans modules, detects incompatibilities with Hyvä theme framework,
* and provides formatted results with summary statistics.
*/
class CompatibilityChecker
{
public function __construct(
Expand All @@ -21,6 +27,11 @@ public function __construct(
/**
* Check all modules for Hyvä compatibility
*
* @param SymfonyStyle $io Symfony Style IO for output
* @param OutputInterface $output Console output interface
* @param bool $showAll Whether to show all modules (including compatible ones)
* @param bool $thirdPartyOnly Whether to scan only third-party modules (excludes Magento_* modules)
* @param bool $excludeVendor Whether to exclude modules from the vendor/ directory (true = exclude, false = include)
* @return array Results with structure: ['modules' => [], 'summary' => [], 'hasIncompatibilities' => bool]
*/
public function check(
Expand Down Expand Up @@ -90,7 +101,9 @@ public function check(
}

$results['summary']['criticalIssues'] += $scanResult['criticalIssues'];
$results['summary']['warningIssues'] += ($scanResult['totalIssues'] - $scanResult['criticalIssues']);
// Calculate warnings explicitly to support future severity levels
$warningCount = max(0, $scanResult['totalIssues'] - $scanResult['criticalIssues']);
$results['summary']['warningIssues'] += $warningCount;
}

return $results;
Expand Down Expand Up @@ -172,7 +185,8 @@ private function getIssuesDisplay(array $moduleData): string
$parts[] = sprintf('<fg=red>%d critical</>', $scanResult['criticalIssues']);
}

$warnings = $scanResult['totalIssues'] - $scanResult['criticalIssues'];
// Calculate warnings explicitly to ensure non-negative values
$warnings = max(0, $scanResult['totalIssues'] - $scanResult['criticalIssues']);
if ($warnings > 0) {
$parts[] = sprintf('<fg=yellow>%d warning(s)</>', $warnings);
}
Expand All @@ -185,7 +199,17 @@ private function getIssuesDisplay(array $moduleData): string
*/
public function getDetailedIssues(string $moduleName, array $moduleData): array
{
$files = $moduleData['scanResult']['files'] ?? [];
// Safely access nested array structure
$scanResult = $moduleData['scanResult'] ?? [];
if (!is_array($scanResult)) {
return [];
}

$files = $scanResult['files'] ?? [];
if (!is_array($files)) {
return [];
}

$details = [];

foreach ($files as $filePath => $issues) {
Expand Down
6 changes: 6 additions & 0 deletions src/Service/Hyva/IncompatibilityDetector.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@

use Magento\Framework\Filesystem\Driver\File;

/**
* Service that detects Hyvä incompatibility patterns in JavaScript, XML, and PHTML files
*
* Uses pattern matching to identify RequireJS, Knockout.js, jQuery, and UI Components
* usage that would be problematic in a Hyvä environment.
*/
class IncompatibilityDetector
{
private const SEVERITY_CRITICAL = 'critical';
Expand Down
79 changes: 61 additions & 18 deletions src/Service/Hyva/ModuleScanner.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@

use Magento\Framework\Filesystem\Driver\File;

/**
* Service that scans module directories for files containing Hyvä compatibility issues
*
* Recursively scans JavaScript, XML, and PHTML files within module directories
* to identify patterns that may be incompatible with Hyvä themes.
*/
class ModuleScanner
{
private const SCAN_EXTENSIONS = ['js', 'xml', 'phtml'];
Expand Down Expand Up @@ -87,14 +93,45 @@ private function findRelevantFiles(string $directory): array
}
}
} catch (\Exception $e) {
// Silently skip directories that can't be read
// Skip directories that can't be read, but log details when debugging
if (getenv('MAGEFORGE_DEBUG')) {
error_log(sprintf(
'[MageForge][ModuleScanner] Failed to read directory "%s": %s',
$directory,
$e->getMessage()
));
}
}

return $relevantFiles;
}

/**
* Check if module has Hyvä compatibility package
* Check if module has Hyvä compatibility package based on composer data
*
* @param array $composerData Parsed composer.json data
*/
private function isHyvaCompatibilityPackage(array $composerData): bool
{
// Check if this IS a Hyvä compatibility package
$packageName = $composerData['name'] ?? '';
if (str_starts_with($packageName, 'hyva-themes/') && str_contains($packageName, '-compat')) {
return true;
}

// Check dependencies for Hyvä packages
$requires = $composerData['require'] ?? [];
foreach ($requires as $package => $version) {
if (str_starts_with($package, 'hyva-themes/')) {
return true;
}
}

return false;
}

/**
* Check if module has Hyvä compatibility package (public wrapper)
*/
public function hasHyvaCompatibilityPackage(string $modulePath): bool
{
Expand All @@ -112,24 +149,18 @@ public function hasHyvaCompatibilityPackage(string $modulePath): bool
return false;
}

// Check if this IS a Hyvä compatibility package
$packageName = $composerData['name'] ?? '';
if (str_starts_with($packageName, 'hyva-themes/') && str_contains($packageName, '-compat')) {
return true;
}

// Check dependencies for Hyvä packages
$requires = $composerData['require'] ?? [];
foreach ($requires as $package => $version) {
if (str_starts_with($package, 'hyva-themes/')) {
return true;
}
}
return $this->isHyvaCompatibilityPackage($composerData);
} catch (\Exception $e) {
// Log error when debugging to help identify JSON or file read issues
if (getenv('MAGEFORGE_DEBUG')) {
error_log(sprintf(
'[MageForge][ModuleScanner] Failed to read composer.json at "%s": %s',
$composerPath,
$e->getMessage()
));
}
return false;
}

return false;
}

/**
Expand All @@ -147,12 +178,24 @@ public function getModuleInfo(string $modulePath): array
$content = $this->fileDriver->fileGetContents($composerPath);
$composerData = json_decode($content, true);

if (!is_array($composerData)) {
return ['name' => 'Unknown', 'version' => 'Unknown', 'isHyvaAware' => false];
}

return [
'name' => $composerData['name'] ?? 'Unknown',
'version' => $composerData['version'] ?? 'Unknown',
'isHyvaAware' => $this->hasHyvaCompatibilityPackage($modulePath),
'isHyvaAware' => $this->isHyvaCompatibilityPackage($composerData),
];
} catch (\Exception $e) {
// Log error when debugging to help identify JSON parsing or file read issues
if (getenv('MAGEFORGE_DEBUG')) {
error_log(sprintf(
'[MageForge][ModuleScanner] Failed to read module info at "%s": %s',
$composerPath,
$e->getMessage()
));
}
return ['name' => 'Unknown', 'version' => 'Unknown', 'isHyvaAware' => false];
}
}
Expand Down