feat: VivesPOS landing on Winter CMS 1.2 — theme + plugin + Dockerfile
Some checks are pending
Module sub-split / Sub-split (push) Waiting to run
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
This commit is contained in:
263
modules/system/console/asset/AssetCreate.php
Normal file
263
modules/system/console/asset/AssetCreate.php
Normal file
@@ -0,0 +1,263 @@
|
||||
<?php
|
||||
|
||||
namespace System\Console\Asset;
|
||||
|
||||
use Closure;
|
||||
use Cms\Classes\Theme;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use System\Classes\Asset\BundleManager;
|
||||
use System\Classes\Asset\PackageJson;
|
||||
use System\Classes\Asset\PackageManager;
|
||||
use System\Classes\PluginManager;
|
||||
use Winter\Storm\Console\Command;
|
||||
use Winter\Storm\Support\Facades\File;
|
||||
|
||||
abstract class AssetCreate extends Command
|
||||
{
|
||||
protected const TYPE_MODULE = 'module';
|
||||
protected const TYPE_PLUGIN = 'plugin';
|
||||
protected const TYPE_THEME = 'theme';
|
||||
|
||||
/**
|
||||
* @var string The console command description.
|
||||
*/
|
||||
protected $description = 'Creates the compiler configuration files for the provided package and optionally installs the necessary dependencies for any selected asset bundles.';
|
||||
|
||||
/**
|
||||
* Local cache of fixture path
|
||||
*/
|
||||
private string $fixturePath;
|
||||
|
||||
/**
|
||||
* The type of compilable to configure
|
||||
*/
|
||||
protected string $assetType;
|
||||
|
||||
/**
|
||||
* The name of the config file
|
||||
*/
|
||||
protected string $configFile;
|
||||
|
||||
/**
|
||||
* Dynamically generate options for all available bundles
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
foreach (BundleManager::instance()->getBundles() as $bundle) {
|
||||
$this->addOption($bundle, null, InputOption::VALUE_NONE, 'Create ' . $bundle . ' configuration');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
$package = $this->argument('packageName');
|
||||
|
||||
$this->fixturePath = __DIR__ . '/fixtures/config';
|
||||
|
||||
$compilableAssets = PackageManager::instance();
|
||||
$compilableAssets->fireCallbacks();
|
||||
|
||||
$packages = $compilableAssets->getPackages($this->assetType, true);
|
||||
|
||||
if (
|
||||
// We have the package already
|
||||
isset($packages[$package])
|
||||
// If the user has requested `force` & `no-interaction` then we do not ask for confirmation and continue
|
||||
&& !($this->option('force') && $this->option('no-interaction'))
|
||||
// If the user has forced but with interaction, then we do not ask for confirmation, else we do
|
||||
&& !($this->option('force') || $this->confirm('Package `' . $package . '` has already been configured, are you sure you wish to continue?'))
|
||||
) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
[$path, $type] = $this->getPackagePathType($package);
|
||||
|
||||
if (is_null($path) || is_null($type)) {
|
||||
$this->error('Package `' . $package . '` could not be resolved');
|
||||
return 1;
|
||||
}
|
||||
|
||||
$packageJson = new PackageJson($path . '/package.json');
|
||||
if (!$packageJson->getName()) {
|
||||
$packageJson->setName(strtolower(str_replace('.', '-', $package)));
|
||||
}
|
||||
|
||||
$this->installConfigs($packageJson, $package, $type, $path);
|
||||
|
||||
$verb = File::exists($packageJson->getPath()) ? 'updated' : 'generated';
|
||||
$packageJson->save();
|
||||
|
||||
if (!$this->option('silent')) {
|
||||
$this->warn("File $verb: " . str_after($packageJson->getPath(), base_path()));
|
||||
$this->info(ucfirst($this->assetType) . ' configuration complete.');
|
||||
}
|
||||
|
||||
$this->afterExecution();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the path and type of the package by name
|
||||
*/
|
||||
protected function getPackagePathType(string $package): array
|
||||
{
|
||||
if (str_starts_with($package, 'theme-')) {
|
||||
if (($theme = Theme::load(str_after($package, 'theme-'))) && File::exists($theme->getPath())) {
|
||||
return [$theme->getPath(), static::TYPE_THEME];
|
||||
}
|
||||
|
||||
return [null, null];
|
||||
}
|
||||
|
||||
if (str_starts_with($package, 'module-')) {
|
||||
if (
|
||||
($modulePath = base_path('modules') . '/' . str_after($package, 'module-'))
|
||||
&& File::exists($modulePath)
|
||||
) {
|
||||
return [$modulePath, static::TYPE_MODULE];
|
||||
}
|
||||
|
||||
return [null, null];
|
||||
}
|
||||
|
||||
if ($plugin = PluginManager::instance()->findByIdentifier($package)) {
|
||||
return [$plugin->getPluginPath(), static::TYPE_PLUGIN];
|
||||
}
|
||||
|
||||
return [null, null];
|
||||
}
|
||||
|
||||
/**
|
||||
* Write out config files based on assetType and the requested options
|
||||
*/
|
||||
protected function installConfigs(
|
||||
PackageJson $packageJson,
|
||||
string $packageName,
|
||||
string $packageType,
|
||||
string $packagePath
|
||||
): void {
|
||||
// Normalize package name
|
||||
$packageName = $this->makePackageName($packageName);
|
||||
// Bind the bundleManager instance
|
||||
$bundleManager = BundleManager::instance();
|
||||
|
||||
// Get the default config
|
||||
$config = $this->getFixture(
|
||||
$this->assetType . '/' . pathinfo($this->configFile, PATHINFO_BASENAME) . '.fixture'
|
||||
);
|
||||
|
||||
// For each bundle offered by node packages
|
||||
foreach ($bundleManager->getBundles() as $bundle) {
|
||||
// If the bundle was not selected exit
|
||||
if (!$this->option($bundle)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Require all packages specified by the bundle
|
||||
foreach ($bundleManager->getBundlePackages($bundle, $this->assetType) as $dependency => $version) {
|
||||
$packageJson->addDependency($dependency, $version, dev: true);
|
||||
}
|
||||
|
||||
// Fire any setup handlers required
|
||||
$setupHandler = $bundleManager->getSetupHandler($bundle);
|
||||
if ($setupHandler) {
|
||||
Closure::bind($setupHandler, $this)->call($this, $packagePath, $packageType);
|
||||
}
|
||||
|
||||
// Loop through all the scaffold handlers to build configs / stubs
|
||||
$scaffoldHandler = $bundleManager->getScaffoldHandler($bundle);
|
||||
if ($scaffoldHandler) {
|
||||
// Generate the config
|
||||
$config = Closure::bind($scaffoldHandler, $this)->call($this, $config, $this->assetType);
|
||||
// Generate stub files if required
|
||||
if (!$this->option('no-stubs')) {
|
||||
$css = Closure::bind($scaffoldHandler, $this)->call($this, $css ?? '', 'css');
|
||||
$js = Closure::bind($scaffoldHandler, $this)->call($this, $js ?? '', 'js');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create stub files if required
|
||||
if (!$this->option('no-stubs')) {
|
||||
foreach (['css', 'js'] as $asset) {
|
||||
// Create asset dist dir so laravel-vite-plugin doesn't complain
|
||||
if (!File::exists($packagePath . '/assets/dist')) {
|
||||
File::makeDirectory($packagePath . '/assets/dist/', recursive: true);
|
||||
}
|
||||
// Create asset src dirs for stubs
|
||||
if (!File::exists($packagePath . '/assets/src/' . $asset)) {
|
||||
File::makeDirectory($packagePath . '/assets/src/' . $asset, recursive: true);
|
||||
}
|
||||
$this->writeFile(
|
||||
sprintf('%1$s/assets/src/%2$s/%3$s.%2$s', $packagePath, $asset, $packageName),
|
||||
$$asset ?? null ? $$asset : $this->getFixture(sprintf('%1$s/default.%1$s.fixture', $asset))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Write out the config file
|
||||
$this->writeFile($packagePath . '/' . $this->configFile, str_replace(
|
||||
'{{packageName}}',
|
||||
$packageName,
|
||||
$config
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a file but ask for conformation before overwriting
|
||||
*/
|
||||
protected function writeFile(string $path, string $content): int
|
||||
{
|
||||
if (
|
||||
// If forced, ignore file existing and overwrite
|
||||
(!$this->option('force') && File::exists($path))
|
||||
&& (
|
||||
// If no interaction requested, then skip the confirm check and return
|
||||
$this->option('no-interaction')
|
||||
// else ask the user if they want overwriting
|
||||
|| !$this->confirm(sprintf('%s already exists, overwrite?', basename($path)))
|
||||
)
|
||||
) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
File::ensureDirectoryExists(pathinfo($path, PATHINFO_DIRNAME));
|
||||
$result = File::put($path, $content);
|
||||
|
||||
if (!$this->option('silent')) {
|
||||
$this->warn('File generated: ' . str_after($path, base_path()));
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for loading fixtures from the default library
|
||||
*/
|
||||
protected function getFixture(string $path): string
|
||||
{
|
||||
return File::get($this->fixturePath . '/' . $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the user supplied package name into a consistent internal format
|
||||
*/
|
||||
protected function makePackageName(string $package): string
|
||||
{
|
||||
return strtolower(str_replace('.', '-', $package));
|
||||
}
|
||||
|
||||
/**
|
||||
* Ran after configuration is complete, use for tearing down / reporting
|
||||
*/
|
||||
protected function afterExecution(): void
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user