Some checks are pending
Module sub-split / Sub-split (push) Waiting to run
- Base: wintercms/winter branch 1.2 (full framework) - Theme vivespos: Canvas 7 + Bootstrap 5 CDN, custom CSS - Layout: deferred GTM/GA4 tracking, JSON-LD SoftwareApplication - Partials: hero (offline-first), features, modes (offline/nube toggle), screenshots, pricing (3 planes), comparison, FAQ, CTA - Plugin VivesPOS.Site with ContactForm - Dockerfile: PHP 8.2 Apache, port 80, healthcheck - Added winter/wn-pages, blog, sitemap, seo plugins - Active theme set to vivespos
273 lines
8.2 KiB
PHP
273 lines
8.2 KiB
PHP
<?php
|
|
|
|
namespace System\Console\Asset;
|
|
|
|
use Symfony\Component\Process\Process;
|
|
use System\Classes\Asset\PackageManager;
|
|
use System\Classes\Asset\PackageJson;
|
|
use Winter\Storm\Console\Command;
|
|
use Winter\Storm\Support\Facades\File;
|
|
use Winter\Storm\Support\Str;
|
|
|
|
abstract class AssetCompile extends Command
|
|
{
|
|
/**
|
|
* @var string The console command description.
|
|
*/
|
|
protected $description = 'Mix and compile assets';
|
|
|
|
/**
|
|
* PackageJson object holding the contents of the active package.json
|
|
*/
|
|
protected PackageJson $packageJson;
|
|
|
|
/**
|
|
* Name of config file i.e. mix.webpack.js, vite.config.js
|
|
*/
|
|
protected string $configFile;
|
|
|
|
/**
|
|
* File path being watched, used for cleanup by mix:watch
|
|
*/
|
|
protected string $watchingFilePath;
|
|
|
|
public function compileHandle(string $type): int
|
|
{
|
|
// Exit early if node_modules isn't available yet
|
|
if (!File::exists(base_path('node_modules'))) {
|
|
$this->error(sprintf(
|
|
'The Node dependencies are not available, try running %s:install first.',
|
|
$type
|
|
));
|
|
return 1;
|
|
}
|
|
|
|
$compilableAssets = PackageManager::instance();
|
|
$compilableAssets->fireCallbacks();
|
|
|
|
$registeredPackages = $compilableAssets->getPackages($type);
|
|
$requestedPackages = $this->option('package') ?: [];
|
|
|
|
// Calling commands in unit tests can cause the option casting to not work correctly,
|
|
// ensure that the option value is always an array
|
|
if (is_string($requestedPackages)) {
|
|
$requestedPackages = [$requestedPackages];
|
|
}
|
|
|
|
// Normalize the requestedPackages option
|
|
if (count($requestedPackages)) {
|
|
foreach ($requestedPackages as &$name) {
|
|
$name = strtolower($name);
|
|
}
|
|
unset($name);
|
|
}
|
|
|
|
// Filter the registered packages to only include requested packages
|
|
if (count($requestedPackages) && count($registeredPackages)) {
|
|
// Get an updated list of packages including any newly added packages
|
|
$registeredPackages = $compilableAssets->getPackages($type);
|
|
|
|
// Filter the registered packages to only deal with the requested packages
|
|
foreach (array_keys($registeredPackages) as $name) {
|
|
if (!in_array($name, $requestedPackages)) {
|
|
unset($registeredPackages[$name]);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!count($registeredPackages)) {
|
|
if (count($requestedPackages)) {
|
|
$this->error('No registered packages matched the requested packages for compilation.');
|
|
return 1;
|
|
} else {
|
|
$this->info('No packages registered for mixing.');
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
$exits = [];
|
|
foreach ($registeredPackages as $name => $package) {
|
|
$relativeMixJsPath = $package['config'];
|
|
if (!$this->isPackageWithinWorkspace($relativeMixJsPath)) {
|
|
$this->error(sprintf(
|
|
'Unable to compile "%s", %s was not found in the package.json\'s workspaces.packages property.'
|
|
. ' Try running %s:install first.',
|
|
$name,
|
|
$relativeMixJsPath,
|
|
$type
|
|
));
|
|
continue;
|
|
}
|
|
|
|
if (!$this->option('silent')) {
|
|
$this->info(sprintf('Compiling package "%s"', $name));
|
|
}
|
|
|
|
$exitCode = $this->executeProcess(base_path($relativeMixJsPath));
|
|
|
|
if ($exitCode > 0) {
|
|
$this->error(sprintf('Unable to compile package "%s"', $name));
|
|
}
|
|
|
|
if ($this->option('stop-on-error') && $exitCode > 0) {
|
|
return $exitCode;
|
|
}
|
|
|
|
$exits[] = $exitCode;
|
|
}
|
|
|
|
return (int) !empty(array_filter($exits));
|
|
}
|
|
|
|
public function watchHandle(string $type): int
|
|
{
|
|
$compilableAssets = PackageManager::instance();
|
|
$compilableAssets->fireCallbacks();
|
|
|
|
$packages = $compilableAssets->getPackages($type);
|
|
$name = $this->argument('package');
|
|
$nameLower = strtolower($name);
|
|
|
|
if (!in_array($nameLower, array_keys($packages))) {
|
|
$this->error(
|
|
sprintf('Package "%s" is not a registered package.', $name)
|
|
);
|
|
return 1;
|
|
}
|
|
|
|
$package = $packages[$nameLower];
|
|
|
|
$relativeConfigPath = $package['config'];
|
|
if (!$this->isPackageWithinWorkspace($relativeConfigPath)) {
|
|
$this->error(sprintf(
|
|
'Unable to watch "%s", %s was not found in the package.json\'s workspaces.packages property. Try running %s:install first.',
|
|
$name,
|
|
$relativeConfigPath,
|
|
$type
|
|
));
|
|
return 1;
|
|
}
|
|
|
|
if (!$this->option('silent')) {
|
|
$this->info(sprintf('Watching package "%s" for changes', $name));
|
|
}
|
|
|
|
$this->watchingFilePath = $relativeConfigPath;
|
|
|
|
if ($this->executeProcess(base_path($relativeConfigPath)) !== 0) {
|
|
$this->error(sprintf('Unable to compile package "%s"', $name));
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Get the package path for the provided winter.mix.js file
|
|
*/
|
|
protected function getPackagePath(string $path): string
|
|
{
|
|
return pathinfo($path, PATHINFO_DIRNAME);
|
|
}
|
|
|
|
/**
|
|
* Get the path to the mix.webpack.js file for the provided winter.mix.js file
|
|
*/
|
|
protected function getJsConfigPath(string $path): string
|
|
{
|
|
return $this->getPackagePath($path) . DIRECTORY_SEPARATOR . $this->configFile;
|
|
}
|
|
|
|
/**
|
|
* Check if Mix is able to compile the provided winter.mix.js file
|
|
*/
|
|
protected function isPackageWithinWorkspace(string $mixJsPath): bool
|
|
{
|
|
if (!isset($this->packageJson)) {
|
|
// Load the main package.json for the project
|
|
$this->packageJson = $this->getNpmPackageManifest();
|
|
}
|
|
|
|
return $this->packageJson->hasWorkspace(
|
|
Str::replace(DIRECTORY_SEPARATOR, '/', $this->getPackagePath($mixJsPath))
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Read the package.json file for the project, path configurable with the
|
|
* `--manifest` option
|
|
*/
|
|
protected function getNpmPackageManifest(): PackageJson
|
|
{
|
|
return new PackageJson(base_path($this->option('manifest') ?? 'package.json'));
|
|
}
|
|
|
|
/**
|
|
* Run the mix command against the provided package
|
|
*/
|
|
protected function executeProcess(string $configPath): int
|
|
{
|
|
$this->beforeExecution($configPath);
|
|
$command = $this->createCommand($configPath);
|
|
$commandEnv = $this->createCommandEnv($configPath);
|
|
|
|
$process = new Process(
|
|
$command,
|
|
$this->getPackagePath($configPath),
|
|
[
|
|
'NODE_ENV' => $this->option('production', false) ? 'production' : 'development',
|
|
...$commandEnv
|
|
],
|
|
null,
|
|
null
|
|
);
|
|
|
|
if (!$this->option('disable-tty')) {
|
|
try {
|
|
$process->setTty(true);
|
|
} catch (\Throwable $e) {
|
|
// This will fail on unsupported systems
|
|
}
|
|
}
|
|
|
|
$exitCode = $process->run(function ($status, $stdout) {
|
|
if (!$this->option('silent')) {
|
|
$this->getOutput()->write($stdout);
|
|
}
|
|
});
|
|
|
|
$this->afterExecution($configPath);
|
|
|
|
return $exitCode;
|
|
}
|
|
|
|
/**
|
|
* Ran before dispatching the compile process, use for setting up
|
|
*/
|
|
protected function beforeExecution(string $configPath): void
|
|
{
|
|
// do nothing
|
|
}
|
|
|
|
/**
|
|
* Ran after dispatching the compile process, use for tearing down
|
|
*/
|
|
protected function afterExecution(string $configPath): void
|
|
{
|
|
// do nothing
|
|
}
|
|
|
|
/**
|
|
* Create the command array to create a Process object with
|
|
*/
|
|
abstract protected function createCommand(string $configPath): array;
|
|
|
|
/**
|
|
* Return values to append to the command env
|
|
*/
|
|
protected function createCommandEnv(string $configPath): array
|
|
{
|
|
return [];
|
|
}
|
|
}
|