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:
272
modules/system/console/asset/AssetCompile.php
Normal file
272
modules/system/console/asset/AssetCompile.php
Normal file
@@ -0,0 +1,272 @@
|
||||
<?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 [];
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
409
modules/system/console/asset/AssetInstall.php
Normal file
409
modules/system/console/asset/AssetInstall.php
Normal file
@@ -0,0 +1,409 @@
|
||||
<?php
|
||||
|
||||
namespace System\Console\Asset;
|
||||
|
||||
use Cms\Classes\Theme;
|
||||
use Symfony\Component\Process\Exception\ProcessSignaledException;
|
||||
use Symfony\Component\Process\Process;
|
||||
use System\Classes\Asset\PackageJson;
|
||||
use System\Classes\Asset\PackageManager;
|
||||
use System\Classes\PluginManager;
|
||||
use Winter\Storm\Console\Command;
|
||||
use Winter\Storm\Exception\SystemException;
|
||||
use Winter\Storm\Support\Facades\Config;
|
||||
use Winter\Storm\Support\Facades\File;
|
||||
use Winter\Storm\Support\Str;
|
||||
|
||||
abstract class AssetInstall extends Command
|
||||
{
|
||||
/**
|
||||
* The path to the "npm" executable.
|
||||
*/
|
||||
protected string $npmPath = 'npm';
|
||||
|
||||
/**
|
||||
* Terms used in messages.
|
||||
*/
|
||||
protected array $terms = [
|
||||
'complete' => 'install',
|
||||
'completed' => 'installed',
|
||||
];
|
||||
|
||||
/**
|
||||
* Path to package json, if null use base_path.
|
||||
*/
|
||||
protected ?string $packageJsonPath = null;
|
||||
|
||||
/**
|
||||
* Type of asset to be installed, @see PackageManager
|
||||
*/
|
||||
protected string $assetType;
|
||||
|
||||
/**
|
||||
* The asset config file
|
||||
*/
|
||||
protected string $configFile;
|
||||
|
||||
/**
|
||||
* The required dependencies for this compiler
|
||||
*/
|
||||
protected array $requiredDependencies = [];
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
if ($npmPath = $this->option('npm')) {
|
||||
if (!File::exists($npmPath) || !is_executable($npmPath)) {
|
||||
$this->error('The supplied --npm path does not exist or is not executable.');
|
||||
return 1;
|
||||
}
|
||||
$this->npmPath = $npmPath;
|
||||
}
|
||||
|
||||
if (!version_compare($this->getNpmVersion(), '7', '>')) {
|
||||
$this->error('"npm" version 7 or above must be installed to run this command.');
|
||||
return 1;
|
||||
}
|
||||
|
||||
// If a custom path is passed, then validate it
|
||||
if ($packageJsonPath = $this->option('package-json')) {
|
||||
// If this is not an absolute path, then make it relative
|
||||
if (!str_starts_with($packageJsonPath, '/')) {
|
||||
$packageJsonPath = base_path($packageJsonPath);
|
||||
}
|
||||
|
||||
if (!File::exists($packageJsonPath)) {
|
||||
$this->error('The supplied --package-json path does not exist.');
|
||||
return 1;
|
||||
}
|
||||
|
||||
$this->packageJsonPath = $packageJsonPath;
|
||||
}
|
||||
|
||||
// Get any packages the user has requested
|
||||
$requestedPackages = $this->argument('assetPackage') ?: [];
|
||||
|
||||
$registeredPackages = $this->getRegisteredPackages($requestedPackages);
|
||||
|
||||
if (!$registeredPackages) {
|
||||
if ($requestedPackages) {
|
||||
$this->error('No registered packages matched the requested packages for installation.');
|
||||
return 1;
|
||||
}
|
||||
|
||||
$this->info('No packages registered for mixing.');
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Get base package.json
|
||||
$packageJson = new PackageJson($this->packageJsonPath ?? base_path('package.json'));
|
||||
|
||||
// Ensure asset compiling packages are set in package.json, then save
|
||||
$this->validateRequireDependenciesPresent($packageJson)
|
||||
->save();
|
||||
|
||||
// Process compilable asset packages, then save
|
||||
$this->processPackages($registeredPackages, $packageJson)
|
||||
->save();
|
||||
|
||||
if (!$this->option('no-install')) {
|
||||
// Ensure separation between package.json modification messages and rest of output
|
||||
$this->info('');
|
||||
|
||||
if ($this->runNpmInstall() !== 0) {
|
||||
$this->error("Unable to {$this->terms['complete']} dependencies.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
$this->info("Dependencies successfully {$this->terms['completed']}!");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all packages registered by the system filtered by requestedPackages if defined
|
||||
* @throws SystemException
|
||||
*/
|
||||
protected function getRegisteredPackages(array $requestedPackages = []): array
|
||||
{
|
||||
$packageManager = $this->getPackageManager();
|
||||
|
||||
$registeredPackages = $packageManager->getPackages($this->assetType, true);
|
||||
|
||||
// Normalize the requestedPackages option
|
||||
$requestedPackages = array_map(fn ($name) => strtolower($name), $requestedPackages);
|
||||
|
||||
// Filter the registered packages to only include requested packages
|
||||
if (count($requestedPackages) && count($registeredPackages)) {
|
||||
$cmsEnabled = in_array('Cms', Config::get('cms.loadModules'));
|
||||
|
||||
// Autogenerate config files for packages that don't exist but can be autodiscovered
|
||||
foreach ($requestedPackages as $package) {
|
||||
// Check if the package is already registered
|
||||
if (isset($registeredPackages[$package])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch ($packageManager->getPackageTypeFromName($package)) {
|
||||
case PackageManager::TYPE_MODULE:
|
||||
$packageManager->registerPackage(
|
||||
$package,
|
||||
base_path('modules/' . Str::after($package, 'module-') . '/' . $this->configFile),
|
||||
$this->assetType
|
||||
);
|
||||
break;
|
||||
case PackageManager::TYPE_THEME:
|
||||
if (!$cmsEnabled) {
|
||||
break;
|
||||
}
|
||||
$theme = Theme::load(Str::after($package, 'theme-'));
|
||||
$packageManager->registerPackage(
|
||||
$package,
|
||||
$theme->getPath() . '/' . $this->configFile,
|
||||
$this->assetType
|
||||
);
|
||||
break;
|
||||
case PackageManager::TYPE_PLUGIN:
|
||||
$packageManager->registerPackage(
|
||||
$package,
|
||||
PluginManager::instance()->getPluginPath($package) . '/' . $this->configFile,
|
||||
$this->assetType
|
||||
);
|
||||
break;
|
||||
case null:
|
||||
throw new SystemException(sprintf(
|
||||
'PackageNotFoundException: The package `%s` does not exist.',
|
||||
$package
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Get an updated list of packages including any newly added packages
|
||||
$registeredPackages = $packageManager->getPackages($this->assetType, true);
|
||||
|
||||
// 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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $registeredPackages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the package.json of a package has the dependencies required for this command and asks the user if
|
||||
* they want to install them if not present.
|
||||
*/
|
||||
protected function validateRequireDependenciesPresent(PackageJson $packageJson): PackageJson
|
||||
{
|
||||
// Check to see if required packages are already present as a dependency
|
||||
foreach ($this->requiredDependencies as $dependency => $version) {
|
||||
if (
|
||||
!$packageJson->hasDependency($dependency)
|
||||
&& $this->confirm($dependency . ' was not found as a dependency in package.json, would you like to add it?', true)
|
||||
) {
|
||||
$packageJson->addDependency($dependency, $version, dev: true);
|
||||
}
|
||||
}
|
||||
|
||||
return $packageJson;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if the packages passed can be installed and if possible, installs them.
|
||||
* @throws SystemException
|
||||
* @throws PackageNotFoundException
|
||||
*/
|
||||
protected function processPackages(array $registeredPackages, PackageJson $packageJson): PackageJson
|
||||
{
|
||||
// Check if the user requested a specific package for install
|
||||
if ($requestedPackages = array_map(fn ($name) => strtolower($name), $this->argument('assetPackage'))) {
|
||||
$packageManager = $this->getPackageManager();
|
||||
foreach ($requestedPackages as $requestedPackage) {
|
||||
// We did not find the package, exit
|
||||
if (!isset($registeredPackages[$requestedPackage])) {
|
||||
if ($detected = $packageManager->getPackage($requestedPackage, true)) {
|
||||
switch (count($detected)) {
|
||||
case 1:
|
||||
if ($detected[0]['type'] !== $this->assetType) {
|
||||
throw new SystemException(sprintf(
|
||||
'PackageNotConfiguredException: The requested package `%s` is only configured for %s. Run `php artisan %s:create %1$s`',
|
||||
$requestedPackage,
|
||||
$detected[0]['type'],
|
||||
$this->assetType
|
||||
));
|
||||
}
|
||||
|
||||
if ($detected[0]['ignored']) {
|
||||
throw new SystemException(sprintf(
|
||||
'PackageIgnoredException: The requested package `%s` is ignored, remove it from package.json to continue',
|
||||
$requestedPackage,
|
||||
));
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
default:
|
||||
if (($detected[0]['ignored'] ?? false) || ($detected[1]['ignored'] ?? false)) {
|
||||
throw new SystemException(sprintf(
|
||||
'PackageIgnoredException: The requested package `%s` is ignored, remove it from package.json to continue',
|
||||
$requestedPackage,
|
||||
));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
throw new SystemException(sprintf(
|
||||
'PackageNotFoundException: The requested package `%s` could not be found.',
|
||||
$requestedPackage,
|
||||
));
|
||||
}
|
||||
|
||||
$this->processPackage($packageJson, $requestedPackage, $registeredPackages[$requestedPackage], true);
|
||||
}
|
||||
|
||||
return $packageJson;
|
||||
}
|
||||
|
||||
// Process each found package
|
||||
foreach ($registeredPackages as $name => $package) {
|
||||
$this->processPackage($packageJson, $name, $package);
|
||||
}
|
||||
|
||||
return $packageJson;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a package to the project workspace or mark it as ignored based on user input
|
||||
*/
|
||||
protected function processPackage(PackageJson $packageJson, string $name, array $package, bool $force = false): bool
|
||||
{
|
||||
// Normalize package path across OS types
|
||||
$packagePath = Str::replace(DIRECTORY_SEPARATOR, '/', $package['path']);
|
||||
|
||||
// Nicely report if the package is already in the workspace
|
||||
if ($packageJson->hasWorkspace($packagePath)) {
|
||||
$this->warn(sprintf(
|
||||
'Package %s (%s) is already included in workspaces.packages.',
|
||||
$name,
|
||||
$packagePath
|
||||
));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($packageJson->hasIgnoredPackage($packagePath)) {
|
||||
$this->warn(sprintf(
|
||||
'The requested package %s (%s) is ignored, remove it from package.json to continue.',
|
||||
$name,
|
||||
$packagePath
|
||||
));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Add the package path to the instance's package.json->workspaces->packages property if not present
|
||||
if (!$packageJson->hasWorkspace($packagePath) && !$packageJson->hasIgnoredPackage($packagePath)) {
|
||||
if (
|
||||
$force
|
||||
|| $this->confirm(
|
||||
sprintf(
|
||||
"Detected %s (%s), would you like to add it to package.json to include it in your project workspace? Answer no to ignore it.",
|
||||
$name,
|
||||
$packagePath
|
||||
),
|
||||
true
|
||||
)
|
||||
) {
|
||||
$packageJson->addWorkspace($packagePath);
|
||||
$this->info(sprintf(
|
||||
'Adding %s (%s) to the workspaces.packages property in package.json',
|
||||
$name,
|
||||
$packagePath
|
||||
));
|
||||
} else {
|
||||
$packageJson->addIgnoredPackage($packagePath);
|
||||
$this->warn(
|
||||
sprintf('Ignoring %s (%s)', $name, $packagePath)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Detect missing config files and provide feedback
|
||||
if (!File::exists($package['config'])) {
|
||||
$this->info(sprintf(
|
||||
'No config file found for %s, you should run %s:config',
|
||||
$name,
|
||||
$this->assetType
|
||||
));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs the dependencies for the given package.
|
||||
*/
|
||||
protected function runNpmInstall(): int
|
||||
{
|
||||
$process = new Process(
|
||||
command: [$this->npmPath, 'install'],
|
||||
cwd: $this->packageJsonPath ? dirname($this->packageJsonPath) : base_path(),
|
||||
timeout: null
|
||||
);
|
||||
|
||||
if (!$this->option('disable-tty')) {
|
||||
try {
|
||||
$process->setTty(true);
|
||||
} catch (\Throwable $e) {
|
||||
// This will fail on unsupported systems
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
return $process->run(function ($status, $stdout) {
|
||||
if (!$this->option('silent')) {
|
||||
$this->getOutput()->write($stdout);
|
||||
}
|
||||
});
|
||||
} catch (ProcessSignaledException $e) {
|
||||
if (extension_loaded('pcntl') && $e->getSignal() !== SIGINT) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the root package.json as a PackageManager object
|
||||
*/
|
||||
protected function getPackageManager(): PackageManager
|
||||
{
|
||||
// Flush the instance
|
||||
$packageManager = PackageManager::instance()->fireCallbacks();
|
||||
// Ensure the instance follows any custom package.json
|
||||
if ($this->packageJsonPath) {
|
||||
$packageManager->setPackageJsonPath($this->packageJsonPath);
|
||||
}
|
||||
|
||||
return $packageManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the installed NPM version.
|
||||
*/
|
||||
protected function getNpmVersion(): string
|
||||
{
|
||||
$process = new Process([$this->npmPath, '--version']);
|
||||
$process->run();
|
||||
return $process->getOutput();
|
||||
}
|
||||
}
|
||||
62
modules/system/console/asset/AssetList.php
Normal file
62
modules/system/console/asset/AssetList.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace System\Console\Asset;
|
||||
|
||||
use System\Classes\Asset\PackageManager;
|
||||
use Winter\Storm\Console\Command;
|
||||
use Winter\Storm\Support\Facades\File;
|
||||
|
||||
abstract class AssetList extends Command
|
||||
{
|
||||
protected string $assetType;
|
||||
|
||||
public function handle(): int
|
||||
{
|
||||
$compilableAssets = PackageManager::instance();
|
||||
$compilableAssets->fireCallbacks();
|
||||
|
||||
$packages = $compilableAssets->getPackages($this->assetType, true);
|
||||
|
||||
if (count($packages) === 0) {
|
||||
$this->info('No packages have been registered.');
|
||||
return 0;
|
||||
}
|
||||
|
||||
$errors = [];
|
||||
|
||||
$rows = [];
|
||||
foreach ($packages as $name => $package) {
|
||||
$rows[] = [
|
||||
'name' => $name,
|
||||
'active' => !$package['ignored'],
|
||||
'path' => $package['path'],
|
||||
'configuration' => $package['config'],
|
||||
];
|
||||
|
||||
if (!File::exists($package['config'])) {
|
||||
$errors[] = "The config file for $name doesn't exist, try running artisan $this->assetType:install";
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->option('json')) {
|
||||
$this->line(json_encode($rows, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
|
||||
} else {
|
||||
$this->line('');
|
||||
$this->info('Packages registered:');
|
||||
$this->line('');
|
||||
$this->table(['Name', 'Active', 'Path', 'Configuration'], array_map(function ($row) {
|
||||
$row['active'] = ($row['active']) ? '<info>Yes</info>' : '<fg=red>No</>';
|
||||
return $row;
|
||||
}, $rows));
|
||||
$this->line('');
|
||||
}
|
||||
|
||||
if (!empty($errors)) {
|
||||
foreach ($errors as $error) {
|
||||
$this->error($error);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
/**
|
||||
* Css here
|
||||
*/
|
||||
@@ -0,0 +1,3 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@@ -0,0 +1 @@
|
||||
console.log('hello world!');
|
||||
@@ -0,0 +1,8 @@
|
||||
import { createApp } from "vue";
|
||||
// import Example from "./components/Example.vue";
|
||||
//
|
||||
// const app = createApp({
|
||||
// components: {Example}
|
||||
// });
|
||||
//
|
||||
// app.mount("#example");
|
||||
@@ -0,0 +1,4 @@
|
||||
const mix = require('laravel-mix');
|
||||
mix.setPublicPath(__dirname);
|
||||
|
||||
mix.js('assets/src/js/{{packageName}}.js', 'assets/dist/js/{{packageName}}.js');
|
||||
@@ -0,0 +1,7 @@
|
||||
import React from "react";
|
||||
|
||||
const App = () => {
|
||||
return <h1>Hello from React in Winter CMS!</h1>;
|
||||
};
|
||||
|
||||
export default App;
|
||||
@@ -0,0 +1 @@
|
||||
import('./{{packageName}}.jsx');
|
||||
@@ -0,0 +1,5 @@
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import App from "./components/App.jsx";
|
||||
|
||||
ReactDOM.createRoot(document.getElementById("root")).render(<App />);
|
||||
@@ -0,0 +1,5 @@
|
||||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,15 @@
|
||||
import defaultTheme from 'tailwindcss/defaultTheme';
|
||||
import forms from '@tailwindcss/forms';
|
||||
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
content: [
|
||||
'./assets/src/js/**/*.{js,jsx}',
|
||||
'./blocks/**/*.block',
|
||||
'./components/**/*.{htm,php}',
|
||||
'./controllers/**/*.{htm,php}',
|
||||
'./formwidgets/**/*.{htm,php}',
|
||||
'./widgets/**/*.{htm,php}',
|
||||
],
|
||||
plugins: [forms],
|
||||
};
|
||||
@@ -0,0 +1,15 @@
|
||||
import defaultTheme from 'tailwindcss/defaultTheme';
|
||||
import forms from '@tailwindcss/forms';
|
||||
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
content: [
|
||||
'./assets/src/js/**/*.{js,jsx}',
|
||||
'./blocks/**/*.block',
|
||||
'./layouts/**/*.htm',
|
||||
'./pages/**/*.htm',
|
||||
'./partials/**/*.htm',
|
||||
'./content/**/*.htm',
|
||||
],
|
||||
plugins: [forms],
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
import { defineConfig } from 'vite';
|
||||
import laravel from 'laravel-vite-plugin';
|
||||
|
||||
const defaultOutDir = 'assets/dist';
|
||||
|
||||
export default defineConfig({
|
||||
base: `${process.env.VITE_BASE}/${defaultOutDir}`,
|
||||
build: {
|
||||
outDir: defaultOutDir,
|
||||
assetsDir: '',
|
||||
},
|
||||
plugins: [
|
||||
laravel({
|
||||
publicDirectory: defaultOutDir,
|
||||
input: [
|
||||
'assets/src/css/{{packageName}}.css',
|
||||
'assets/src/js/{{packageName}}.js',
|
||||
],
|
||||
refresh: {
|
||||
paths: [
|
||||
'./**/*.htm',
|
||||
'./**/*.block',
|
||||
'assets/src/**/*.css',
|
||||
'assets/src/**/*.js',
|
||||
'assets/src/**/*.jsx',
|
||||
]
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
87
modules/system/console/asset/fixtures/mix.webpack.js.fixture
Normal file
87
modules/system/console/asset/fixtures/mix.webpack.js.fixture
Normal file
@@ -0,0 +1,87 @@
|
||||
const basePath = '%base%';
|
||||
const { assertSupportedNodeVersion } = require(basePath + '/node_modules/laravel-mix/src/Engine');
|
||||
|
||||
module.exports = async () => {
|
||||
assertSupportedNodeVersion();
|
||||
|
||||
const mix = require(basePath + '/node_modules/laravel-mix/src/Mix').primary;
|
||||
|
||||
mix.listen('init', function (_mix) {
|
||||
// Setup Winter path aliases
|
||||
_mix._api.alias({
|
||||
'$': '%pluginsPath%',
|
||||
'~': '%appPath%',
|
||||
});
|
||||
// disable notifications if not in watch
|
||||
%notificationInject%
|
||||
// define options
|
||||
_mix._api.options({
|
||||
processCssUrls: false,
|
||||
clearConsole: false,
|
||||
cssNano: {
|
||||
discardComments: {removeAll: true},
|
||||
}
|
||||
});
|
||||
// enable source maps for dev builds
|
||||
if (!_mix._api.inProduction()) {
|
||||
_mix._api.webpackConfig({
|
||||
devtool: 'inline-source-map'
|
||||
});
|
||||
_mix._api.sourceMaps();
|
||||
}
|
||||
|
||||
// Disable default manifest, allow for custom manifest
|
||||
if (_mix.config.manifest === 'mix-manifest.json') {
|
||||
mix.manifest.refresh = _ => void 0;
|
||||
} else {
|
||||
if (_mix.config.manifest === true) {
|
||||
_mix.config.manifest = 'mix-manifest.json';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// override default mix output
|
||||
mix.listen("loading-plugins", function (plugins) {
|
||||
plugins.forEach(function (plugin, index) {
|
||||
switch (plugin.constructor.name) {
|
||||
case "BuildOutputPlugin":
|
||||
plugins[index].apply = function (compiler) {
|
||||
compiler.hooks.done.tap('BuildOutputPlugin', stats => {
|
||||
if (stats.hasErrors()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.options.clearConsole) {
|
||||
this.clearConsole();
|
||||
}
|
||||
|
||||
let data = stats.toJson({
|
||||
assets: true,
|
||||
builtAt: true,
|
||||
hash: true,
|
||||
performance: true,
|
||||
relatedAssets: this.options.showRelated
|
||||
});
|
||||
|
||||
if (data.assets.length && !%silent%) {
|
||||
console.log(this.statsTable(data));
|
||||
}
|
||||
});
|
||||
};
|
||||
break;
|
||||
case "WebpackBarPlugin":
|
||||
if (%silent% || %noProgress%) {
|
||||
plugins[index].apply = _ => void 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
require('%mixConfigPath%');
|
||||
|
||||
await mix.installDependencies();
|
||||
await mix.init();
|
||||
|
||||
return mix.build();
|
||||
};
|
||||
@@ -0,0 +1,4 @@
|
||||
const mix = require('laravel-mix');
|
||||
mix.setPublicPath(__dirname + '/assets');
|
||||
|
||||
// Your mix configuration below
|
||||
103
modules/system/console/asset/mix/MixCompile.php
Normal file
103
modules/system/console/asset/mix/MixCompile.php
Normal file
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
namespace System\Console\Asset\Mix;
|
||||
|
||||
use System\Console\Asset\AssetCompile;
|
||||
use Winter\Storm\Support\Facades\File;
|
||||
use Winter\Storm\Support\Str;
|
||||
|
||||
class MixCompile extends AssetCompile
|
||||
{
|
||||
/**
|
||||
* @var string|null The default command name for lazy loading.
|
||||
*/
|
||||
protected static $defaultName = 'mix:compile';
|
||||
|
||||
/**
|
||||
* @var string The name and signature of this command.
|
||||
*/
|
||||
protected $signature = 'mix:compile
|
||||
{webpackArgs?* : Arguments to pass through to the Webpack CLI}
|
||||
{--f|production : Runs compilation in "production" mode}
|
||||
{--s|silent : Enables silent mode, no output will be shown.}
|
||||
{--d|disable-tty : Disable tty mode}
|
||||
{--e|stop-on-error : Exit once an error is encountered}
|
||||
{--m|manifest= : Defines package.json to use for compile}
|
||||
{--p|package=* : Defines one or more packages to compile}
|
||||
{--no-progress : Do not show mix progress}';
|
||||
|
||||
/**
|
||||
* @var string The console command description.
|
||||
*/
|
||||
protected $description = 'Mix and compile assets';
|
||||
|
||||
/**
|
||||
* @var array List of commands that this command replaces (aliases)
|
||||
*/
|
||||
protected $replaces = [
|
||||
'mix:build'
|
||||
];
|
||||
|
||||
/**
|
||||
* Name of config file i.e. mix.webpack.js, vite.config.js
|
||||
*/
|
||||
protected string $configFile = 'mix.webpack.js';
|
||||
|
||||
/**
|
||||
* Call the AssetCompile::compileHandle with the mix type
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
return $this->compileHandle('mix');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the command array to create a Process object with
|
||||
*/
|
||||
protected function createCommand(string $configPath): array
|
||||
{
|
||||
$basePath = base_path();
|
||||
$command = $this->argument('webpackArgs') ?? [];
|
||||
array_unshift(
|
||||
$command,
|
||||
$basePath . sprintf('%1$snode_modules%1$s.bin%1$swebpack', DIRECTORY_SEPARATOR),
|
||||
'build',
|
||||
$this->option('silent') ? '--stats=none' : '--progress',
|
||||
'--config=' . $this->getJsConfigPath($configPath)
|
||||
);
|
||||
|
||||
return $command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the temporary mix.webpack.js config file to run webpack with
|
||||
*/
|
||||
protected function beforeExecution(string $configPath): void
|
||||
{
|
||||
$basePath = base_path();
|
||||
$fixture = File::get(__DIR__ . '/../fixtures/mix.webpack.js.fixture');
|
||||
|
||||
$config = Str::swap([
|
||||
'%base%' => addslashes($basePath),
|
||||
'%notificationInject%' => 'mix._api.disableNotifications();',
|
||||
'%mixConfigPath%' => addslashes($configPath),
|
||||
'%pluginsPath%' => addslashes(plugins_path()),
|
||||
'%appPath%' => addslashes(base_path()),
|
||||
'%silent%' => (int) $this->option('silent'),
|
||||
'%noProgress%' => (int) $this->option('no-progress')
|
||||
], $fixture);
|
||||
|
||||
File::put($this->getJsConfigPath($configPath), $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the temporary mix.webpack.js file
|
||||
*/
|
||||
protected function afterExecution(string $configPath): void
|
||||
{
|
||||
$webpackConfigPath = $this->getJsConfigPath($configPath);
|
||||
if (File::exists($webpackConfigPath) && File::isFile($webpackConfigPath)) {
|
||||
File::delete($webpackConfigPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
37
modules/system/console/asset/mix/MixCreate.php
Normal file
37
modules/system/console/asset/mix/MixCreate.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php namespace System\Console\Asset\Mix;
|
||||
|
||||
use System\Console\Asset\AssetCreate;
|
||||
|
||||
class MixCreate extends AssetCreate
|
||||
{
|
||||
/**
|
||||
* @var string|null The default command name for lazy loading.
|
||||
*/
|
||||
protected static $defaultName = 'mix:create';
|
||||
|
||||
/**
|
||||
* @var string The name and signature of this command.
|
||||
*/
|
||||
protected $signature = 'mix:create
|
||||
{packageName : The package name to add configuration for}
|
||||
{--no-stubs : Disable stub file generation}
|
||||
{--s|silent : Enables silent mode, no output will be shown.}
|
||||
{--f|force : Force file overwrites}';
|
||||
|
||||
/**
|
||||
* @var array List of commands that this command replaces (aliases)
|
||||
*/
|
||||
protected $replaces = [
|
||||
'mix:config',
|
||||
];
|
||||
|
||||
/**
|
||||
* The type of compilable to configure
|
||||
*/
|
||||
protected string $assetType = 'mix';
|
||||
|
||||
/**
|
||||
* The name of the config file
|
||||
*/
|
||||
protected string $configFile = 'winter.mix.js';
|
||||
}
|
||||
44
modules/system/console/asset/mix/MixInstall.php
Normal file
44
modules/system/console/asset/mix/MixInstall.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php namespace System\Console\Asset\Mix;
|
||||
|
||||
use System\Console\Asset\AssetInstall;
|
||||
|
||||
class MixInstall extends AssetInstall
|
||||
{
|
||||
/**
|
||||
* @var string|null The default command name for lazy loading.
|
||||
*/
|
||||
protected static $defaultName = 'mix:install';
|
||||
|
||||
/**
|
||||
* @var string The name and signature of this command.
|
||||
*/
|
||||
protected $signature = 'mix:install
|
||||
{assetPackage?* : The asset package name to install.}
|
||||
{--no-install : Tells Winter not to run npm install after config update.}
|
||||
{--npm= : Defines a custom path to the "npm" binary.}
|
||||
{--d|disable-tty : Disable tty mode.}
|
||||
{--s|silent : Enables silent mode, no output will be shown.}
|
||||
{--p|package-json= : Defines a custom path to "package.json" file. Must be above the workspace path.}';
|
||||
|
||||
/**
|
||||
* @var string The console command description.
|
||||
*/
|
||||
protected $description = 'Install Node.js dependencies required for mixed assets';
|
||||
|
||||
/**
|
||||
* The asset compiler being used
|
||||
*/
|
||||
protected string $assetType = 'mix';
|
||||
|
||||
/**
|
||||
* The asset config file
|
||||
*/
|
||||
protected string $configFile = 'winter.mix.js';
|
||||
|
||||
/**
|
||||
* The required packages for this compiler
|
||||
*/
|
||||
protected array $requiredDependencies = [
|
||||
'laravel-mix' => '^6.0.41',
|
||||
];
|
||||
}
|
||||
29
modules/system/console/asset/mix/MixList.php
Normal file
29
modules/system/console/asset/mix/MixList.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace System\Console\Asset\Mix;
|
||||
|
||||
use System\Console\Asset\AssetList;
|
||||
|
||||
class MixList extends AssetList
|
||||
{
|
||||
/**
|
||||
* @var string|null The default command name for lazy loading.
|
||||
*/
|
||||
protected static $defaultName = 'mix:list';
|
||||
|
||||
/**
|
||||
* @var string The name and signature of this command.
|
||||
*/
|
||||
protected $signature = 'mix:list
|
||||
{--json : Output as JSON}';
|
||||
|
||||
/**
|
||||
* @var string The console command description.
|
||||
*/
|
||||
protected $description = 'List all registered Mix packages in this project.';
|
||||
|
||||
/**
|
||||
* The asset compiler being used
|
||||
*/
|
||||
protected string $assetType = 'mix';
|
||||
}
|
||||
66
modules/system/console/asset/mix/MixWatch.php
Normal file
66
modules/system/console/asset/mix/MixWatch.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace System\Console\Asset\Mix;
|
||||
|
||||
class MixWatch extends MixCompile
|
||||
{
|
||||
/**
|
||||
* @var string|null The default command name for lazy loading.
|
||||
*/
|
||||
protected static $defaultName = 'mix:watch';
|
||||
|
||||
/**
|
||||
* @var string The name and signature of this command.
|
||||
*/
|
||||
protected $signature = 'mix:watch
|
||||
{package : Defines the package to watch for changes}
|
||||
{webpackArgs?* : Arguments to pass through to the Webpack CLI}
|
||||
{--f|production : Runs compilation in "production" mode}
|
||||
{--m|manifest= : Defines package.json to use for compile}
|
||||
{--s|silent : Enables silent mode, no output will be shown.}
|
||||
{--d|disable-tty : Disable tty mode}
|
||||
{--no-progress : Do not show mix progress}';
|
||||
|
||||
/**
|
||||
* @var string The console command description.
|
||||
*/
|
||||
protected $description = 'Mix and compile assets on-the-fly as changes are made.';
|
||||
|
||||
/**
|
||||
* @var array List of commands that this command replaces (aliases)
|
||||
*/
|
||||
protected $replaces = [
|
||||
'mix:dev'
|
||||
];
|
||||
|
||||
/**
|
||||
* Call the AssetCompile::watchHandle with the mix type
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
return $this->watchHandle('mix');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the command array to create a Process object with
|
||||
*/
|
||||
protected function createCommand(string $configPath): array
|
||||
{
|
||||
$command = parent::createCommand($configPath);
|
||||
|
||||
// @TODO: Detect Homestead running on Windows to switch to watch-poll-options instead, see https://laravel-mix.com/docs/6.0/cli#polling
|
||||
$command[] = '--watch';
|
||||
|
||||
return $command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the cleanup of this command if a termination signal is received
|
||||
*/
|
||||
public function handleCleanup(): void
|
||||
{
|
||||
$this->newLine();
|
||||
$this->info('Cleaning up: ' . $this->getPackagePath($this->watchingFilePath));
|
||||
$this->afterExecution(base_path($this->watchingFilePath));
|
||||
}
|
||||
}
|
||||
87
modules/system/console/asset/npm/NpmCommand.php
Normal file
87
modules/system/console/asset/npm/NpmCommand.php
Normal file
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
namespace System\Console\Asset\Npm;
|
||||
|
||||
use Symfony\Component\Process\Exception\ProcessSignaledException;
|
||||
use Symfony\Component\Process\Process;
|
||||
use System\Classes\Asset\PackageJson;
|
||||
use System\Classes\Asset\PackageManager;
|
||||
use Winter\Storm\Console\Command;
|
||||
use Winter\Storm\Exception\SystemException;
|
||||
|
||||
abstract class NpmCommand extends Command
|
||||
{
|
||||
/**
|
||||
* Gets a package config and its PackageJson file based on the `package` argument.
|
||||
* @throws SystemException
|
||||
* @throws \JsonException
|
||||
*/
|
||||
protected function getPackage(): ?array
|
||||
{
|
||||
$compilableAssets = PackageManager::instance();
|
||||
$compilableAssets->fireCallbacks();
|
||||
|
||||
$name = $this->argument('package');
|
||||
|
||||
if (!$name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!$compilableAssets->hasPackage($name, true)) {
|
||||
throw new SystemException(sprintf('Package "%s" is not a registered package.', $name));
|
||||
}
|
||||
|
||||
$package = $compilableAssets->getPackage($name, true)[0] ?? [];
|
||||
|
||||
// Assume that packages with matching names have matching package.json files
|
||||
$packageJson = new PackageJson($package['package'] ?? null);
|
||||
|
||||
return [$package, $packageJson];
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a npm process with the command and cwd provided
|
||||
*/
|
||||
protected function npmRun(array $command, string $path): int
|
||||
{
|
||||
$process = new Process(
|
||||
$command,
|
||||
base_path($path),
|
||||
['NODE_ENV' => $this->getNodeEnv()],
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
if (!$this->option('disable-tty') && !$this->option('silent')) {
|
||||
try {
|
||||
$process->setTty(true);
|
||||
} catch (ProcessSignaledException $e) {
|
||||
if (extension_loaded('pcntl') && $e->getSignal() !== SIGINT) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return 1;
|
||||
} catch (\Throwable $e) {
|
||||
// This will fail on unsupported systems
|
||||
}
|
||||
}
|
||||
|
||||
return $process->run(function ($status, $stdout) {
|
||||
if (!$this->option('silent')) {
|
||||
$this->getOutput()->write($stdout);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the env env to provide to node
|
||||
*/
|
||||
protected function getNodeEnv(): string
|
||||
{
|
||||
if (!$this->hasOption('production')) {
|
||||
return 'development';
|
||||
}
|
||||
|
||||
return $this->option('production', false) ? 'production' : 'development';
|
||||
}
|
||||
}
|
||||
55
modules/system/console/asset/npm/NpmInstall.php
Normal file
55
modules/system/console/asset/npm/NpmInstall.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace System\Console\Asset\Npm;
|
||||
|
||||
use System\Console\Asset\Npm\NpmCommand;
|
||||
use Winter\Storm\Exception\SystemException;
|
||||
|
||||
class NpmInstall extends NpmCommand
|
||||
{
|
||||
/**
|
||||
* @var string|null The default command name for lazy loading.
|
||||
*/
|
||||
protected static $defaultName = 'npm:install';
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected $description = 'Install Node.js dependencies for a package';
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected $signature = 'npm:install
|
||||
{package? : The package name to add configuration for}
|
||||
{npmArgs?* : Arguments to pass through to the "npm" binary}
|
||||
{--npm= : Defines a custom path to the "npm" binary}
|
||||
{--d|dev : Install packages in devDependencies}
|
||||
{--s|silent : Silent mode.}
|
||||
{--disable-tty : Disable tty mode}';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
$command = ($this->argument('npmArgs')) ?? [];
|
||||
|
||||
try {
|
||||
[$package, $packageJson] = $this->getPackage();
|
||||
} catch (SystemException $e) {
|
||||
if (!str_contains($e->getMessage(), 'is not a registered package.')) {
|
||||
throw $e;
|
||||
}
|
||||
array_unshift($command, $this->argument('package'));
|
||||
}
|
||||
|
||||
array_unshift($command, 'npm', 'install');
|
||||
|
||||
if ($this->option('dev')) {
|
||||
$command[] = '--save-dev';
|
||||
}
|
||||
|
||||
return $this->npmRun($command, $package['path'] ?? '');
|
||||
}
|
||||
}
|
||||
66
modules/system/console/asset/npm/NpmRun.php
Normal file
66
modules/system/console/asset/npm/NpmRun.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace System\Console\Asset\Npm;
|
||||
|
||||
use System\Console\Asset\Npm\NpmCommand;
|
||||
|
||||
class NpmRun extends NpmCommand
|
||||
{
|
||||
/**
|
||||
* @var string|null The default command name for lazy loading.
|
||||
*/
|
||||
protected static $defaultName = 'npm:run';
|
||||
|
||||
/**
|
||||
* @var string The name and signature of this command.
|
||||
*/
|
||||
protected $signature = 'npm:run
|
||||
{package : Defines the package where the script is located.}
|
||||
{script : The name of the script to run, as defined in the package.json "scripts" config.}
|
||||
{additionalArgs?* : Arguments to pass through to the script being run.}
|
||||
{--f|production : Runs the script in "production" mode.}
|
||||
{--s|silent : Silent mode.}
|
||||
{--disable-tty : Disable tty mode}';
|
||||
|
||||
/**
|
||||
* @var string The console command description.
|
||||
*/
|
||||
protected $description = 'Runs a script in a given package.';
|
||||
|
||||
/**
|
||||
* @var array List of commands that this command replaces (aliases)
|
||||
*/
|
||||
protected $replaces = [
|
||||
'mix:run'
|
||||
];
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
[$package, $packageJson] = $this->getPackage();
|
||||
|
||||
$script = $this->argument('script');
|
||||
|
||||
if (!$packageJson->hasScript($script)) {
|
||||
$this->error(
|
||||
sprintf('Script "%s" is not defined in package "%s".', $script, $this->argument('package'))
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!$this->option('silent')) {
|
||||
$this->info(sprintf('Running script "%s" in package "%s"', $script, $this->argument('package')));
|
||||
}
|
||||
|
||||
$command = ($this->argument('additionalArgs')) ?? [];
|
||||
if (count($command)) {
|
||||
array_unshift($command, 'npm', 'run', $script, '--');
|
||||
} else {
|
||||
array_unshift($command, 'npm', 'run', $script);
|
||||
}
|
||||
|
||||
return $this->npmRun($command, $package['path']);
|
||||
}
|
||||
}
|
||||
68
modules/system/console/asset/npm/NpmUpdate.php
Normal file
68
modules/system/console/asset/npm/NpmUpdate.php
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace System\Console\Asset\Npm;
|
||||
|
||||
use System\Console\Asset\Npm\NpmCommand;
|
||||
use Winter\Storm\Exception\SystemException;
|
||||
|
||||
class NpmUpdate extends NpmCommand
|
||||
{
|
||||
/**
|
||||
* @var string|null The default command name for lazy loading.
|
||||
*/
|
||||
protected static $defaultName = 'npm:update';
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected $description = 'Update Node.js dependencies required for mixed assets';
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected $signature = 'npm:update
|
||||
{package? : The package name to add configuration for.}
|
||||
{npmArgs?* : Arguments to pass through to the "npm" binary.}
|
||||
{--npm= : Defines a custom path to the "npm" binary.}
|
||||
{--a|save : Tell npm to update package.json.}
|
||||
{--s|silent : Silent mode.}
|
||||
{--disable-tty : Disable tty mode}';
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public $replaces = [
|
||||
'mix:update'
|
||||
];
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
$command = ($this->argument('npmArgs')) ?? [];
|
||||
|
||||
try {
|
||||
[$package, $packageJson] = $this->getPackage();
|
||||
} catch (SystemException $e) {
|
||||
if (!str_contains($e->getMessage(), 'is not a registered package.')) {
|
||||
throw $e;
|
||||
}
|
||||
array_unshift($command, $this->argument('package'));
|
||||
}
|
||||
|
||||
$args = ['npm', 'update'];
|
||||
|
||||
if ($this->option('save')) {
|
||||
$args[] = '--save';
|
||||
}
|
||||
|
||||
if (count($command)) {
|
||||
$args[] = '--';
|
||||
}
|
||||
|
||||
array_unshift($command, ...$args);
|
||||
|
||||
return $this->npmRun($command, $package['path'] ?? '');
|
||||
}
|
||||
}
|
||||
74
modules/system/console/asset/npm/NpmVersion.php
Normal file
74
modules/system/console/asset/npm/NpmVersion.php
Normal file
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace System\Console\Asset\Npm;
|
||||
|
||||
use Symfony\Component\Process\Process;
|
||||
use System\Console\Asset\Npm\NpmCommand;
|
||||
|
||||
class NpmVersion extends NpmCommand
|
||||
{
|
||||
const NPM_MINIMUM_SUPPORTED_VERSION = '7.0';
|
||||
|
||||
/**
|
||||
* @var string|null The default command name for lazy loading.
|
||||
*/
|
||||
protected static $defaultName = 'npm:version';
|
||||
|
||||
/**
|
||||
* @var string The name and signature of this command.
|
||||
*/
|
||||
protected $signature = 'npm:version
|
||||
{--c|compatible : Report compatible version via exit code.}
|
||||
{--s|silent : Silent mode.}
|
||||
{--disable-tty : Disable tty mode}';
|
||||
|
||||
/**
|
||||
* @var string The console command description.
|
||||
*/
|
||||
protected $description = 'Runs a script in a given package.';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
$process = new Process(
|
||||
['npm', '--version'],
|
||||
base_path(),
|
||||
['NODE_ENV' => $this->getNodeEnv()],
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
$output = '';
|
||||
|
||||
$exit = $process->run(function ($status, $stdout) use (&$output) {
|
||||
$output .= $stdout;
|
||||
});
|
||||
|
||||
$output = trim($output);
|
||||
|
||||
// Npm failed for some reason, report to user
|
||||
if ($exit !== 0) {
|
||||
$this->error('NPM exited with error: ' . $output);
|
||||
return $exit;
|
||||
}
|
||||
|
||||
// Report the version to user
|
||||
if (!$this->option('silent')) {
|
||||
$this->info($output);
|
||||
}
|
||||
|
||||
// If the user has not requested a compatibility check, then return 0
|
||||
if (!$this->option('compatible')) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If the version of npm is less than the required minimum, then return fail
|
||||
if (version_compare($output, static::NPM_MINIMUM_SUPPORTED_VERSION, '<')) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
79
modules/system/console/asset/vite/ViteCompile.php
Normal file
79
modules/system/console/asset/vite/ViteCompile.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace System\Console\Asset\Vite;
|
||||
|
||||
use System\Console\Asset\AssetCompile;
|
||||
use Winter\Storm\Support\Str;
|
||||
|
||||
class ViteCompile extends AssetCompile
|
||||
{
|
||||
/**
|
||||
* @var string|null The default command name for lazy loading.
|
||||
*/
|
||||
protected static $defaultName = 'vite:compile';
|
||||
|
||||
/**
|
||||
* @var string The name and signature of this command.
|
||||
*/
|
||||
protected $signature = 'vite:compile
|
||||
{viteArgs?* : Arguments to pass through to the Vite CLI}
|
||||
{--f|production : Runs compilation in "production" mode}
|
||||
{--s|silent : Enables silent mode, no output will be shown.}
|
||||
{--d|disable-tty : Disable tty mode}
|
||||
{--e|stop-on-error : Exit once an error is encountered}
|
||||
{--m|manifest= : Defines package.json to use for compile}
|
||||
{--p|package=* : Defines one or more packages to compile}
|
||||
';
|
||||
|
||||
/**
|
||||
* @var string The console command description.
|
||||
*/
|
||||
protected $description = 'Vite and compile assets';
|
||||
|
||||
/**
|
||||
* @var array List of commands that this command replaces (aliases)
|
||||
*/
|
||||
protected $replaces = [
|
||||
'vite:build'
|
||||
];
|
||||
|
||||
/**
|
||||
* Name of config file i.e. mix.webpack.js, vite.config.js
|
||||
*/
|
||||
protected string $configFile = 'vite.config.mjs';
|
||||
|
||||
/**
|
||||
* Call the AssetCompile::compileHandle with the vite type
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
return $this->compileHandle('vite');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the command array to create a Process object with
|
||||
*/
|
||||
protected function createCommand(string $configPath): array
|
||||
{
|
||||
$basePath = base_path();
|
||||
$command = $this->argument('viteArgs') ?? [];
|
||||
array_unshift(
|
||||
$command,
|
||||
$basePath . sprintf('%1$snode_modules%1$s.bin%1$svite', DIRECTORY_SEPARATOR),
|
||||
'build',
|
||||
$this->option('silent') ? '--logLevel=silent' : '',
|
||||
);
|
||||
|
||||
return $command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return values to append to the command env
|
||||
*/
|
||||
protected function createCommandEnv(string $configPath): array
|
||||
{
|
||||
return [
|
||||
'VITE_BASE' => Str::after($this->getPackagePath($configPath), base_path()),
|
||||
];
|
||||
}
|
||||
}
|
||||
64
modules/system/console/asset/vite/ViteCreate.php
Normal file
64
modules/system/console/asset/vite/ViteCreate.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace System\Console\Asset\Vite;
|
||||
|
||||
use System\Console\Asset\AssetCreate;
|
||||
|
||||
class ViteCreate extends AssetCreate
|
||||
{
|
||||
/**
|
||||
* @var string|null The default command name for lazy loading.
|
||||
*/
|
||||
protected static $defaultName = 'vite:create';
|
||||
|
||||
/**
|
||||
* @var string The name and signature of this command.
|
||||
*/
|
||||
protected $signature = 'vite:create
|
||||
{packageName : The package name to add configuration for}
|
||||
{--no-stubs : Disable stub file generation}
|
||||
{--s|silent : Enables silent mode, no output will be shown.}
|
||||
{--f|force : Force file overwrites}';
|
||||
|
||||
/**
|
||||
* @var array List of commands that this command replaces (aliases)
|
||||
*/
|
||||
protected $replaces = [
|
||||
'vite:config',
|
||||
];
|
||||
|
||||
/**
|
||||
* The type of compilable to configure
|
||||
*/
|
||||
protected string $assetType = 'vite';
|
||||
|
||||
/**
|
||||
* The name of the config file
|
||||
*/
|
||||
protected string $configFile = 'vite.config.mjs';
|
||||
|
||||
/**
|
||||
* Output a helpful message with the twig code to set up vite after config generation is complete
|
||||
*/
|
||||
public function afterExecution(): void
|
||||
{
|
||||
if ($this->option('silent')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$packageName = $this->makePackageName($this->argument('packageName'));
|
||||
$this->output->writeln('');
|
||||
$this->info('Add the following to your twig to enable asset loading:');
|
||||
if ($this->option('react')) {
|
||||
$this->output->writeln(sprintf(
|
||||
'<fg=blue>{{ viteReactRefresh(\'%1$s\') }}</>',
|
||||
strtolower($this->argument('packageName'))
|
||||
));
|
||||
}
|
||||
$this->output->writeln(sprintf(
|
||||
'<fg=blue>{{ vite([\'assets/src/css/%1$s.css\', \'assets/src/js/%1$s.js\'], \'%2$s\') }}</>',
|
||||
$packageName,
|
||||
strtolower($this->argument('packageName'))
|
||||
));
|
||||
}
|
||||
}
|
||||
47
modules/system/console/asset/vite/ViteInstall.php
Normal file
47
modules/system/console/asset/vite/ViteInstall.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace System\Console\Asset\Vite;
|
||||
|
||||
use System\Console\Asset\AssetInstall;
|
||||
|
||||
class ViteInstall extends AssetInstall
|
||||
{
|
||||
/**
|
||||
* @var string|null The default command name for lazy loading.
|
||||
*/
|
||||
protected static $defaultName = 'vite:install';
|
||||
|
||||
/**
|
||||
* @var string The name and signature of this command.
|
||||
*/
|
||||
protected $signature = 'vite:install
|
||||
{assetPackage?* : The asset package name to install.}
|
||||
{--no-install : Tells Winter not to run npm install after config update.}
|
||||
{--npm= : Defines a custom path to the "npm" binary.}
|
||||
{--d|disable-tty : Disable tty mode.}
|
||||
{--s|silent : Enables silent mode, no output will be shown.}
|
||||
{--p|package-json= : Defines a custom path to "package.json" file. Must be above the workspace path.}';
|
||||
|
||||
/**
|
||||
* @var string The console command description.
|
||||
*/
|
||||
protected $description = 'Install Node.js dependencies required for vite assets';
|
||||
|
||||
/**
|
||||
* The type of compilable to configure
|
||||
*/
|
||||
protected string $assetType = 'vite';
|
||||
|
||||
/**
|
||||
* The asset config file
|
||||
*/
|
||||
protected string $configFile = 'vite.config.mjs';
|
||||
|
||||
/**
|
||||
* The required packages for this compiler
|
||||
*/
|
||||
protected array $requiredDependencies = [
|
||||
'vite' => '^6.0.0',
|
||||
'laravel-vite-plugin' => '^1.1.0',
|
||||
];
|
||||
}
|
||||
29
modules/system/console/asset/vite/ViteList.php
Normal file
29
modules/system/console/asset/vite/ViteList.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace System\Console\Asset\Vite;
|
||||
|
||||
use System\Console\Asset\AssetList;
|
||||
|
||||
class ViteList extends AssetList
|
||||
{
|
||||
/**
|
||||
* @var string|null The default command name for lazy loading.
|
||||
*/
|
||||
protected static $defaultName = 'vite:list';
|
||||
|
||||
/**
|
||||
* @var string The name and signature of this command.
|
||||
*/
|
||||
protected $signature = 'vite:list
|
||||
{--json : Output as JSON}';
|
||||
|
||||
/**
|
||||
* @var string The console command description.
|
||||
*/
|
||||
protected $description = 'List all registered Vite packages in this project.';
|
||||
|
||||
/**
|
||||
* The asset compiler being used
|
||||
*/
|
||||
protected string $assetType = 'vite';
|
||||
}
|
||||
72
modules/system/console/asset/vite/ViteWatch.php
Normal file
72
modules/system/console/asset/vite/ViteWatch.php
Normal file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace System\Console\Asset\Vite;
|
||||
|
||||
use Winter\Storm\Support\Facades\File;
|
||||
|
||||
class ViteWatch extends ViteCompile
|
||||
{
|
||||
/**
|
||||
* @var string|null The default command name for lazy loading.
|
||||
*/
|
||||
protected static $defaultName = 'vite:watch';
|
||||
|
||||
/**
|
||||
* @var string The name and signature of this command.
|
||||
*/
|
||||
protected $signature = 'vite:watch
|
||||
{package : The package to watch for changes (ex. author.plugin, theme-code, etc)}
|
||||
{viteArgs?* : Arguments to pass through to the Webpack CLI}
|
||||
{--f|production : Runs compilation in "production" mode}
|
||||
{--m|manifest= : Defines package.json to use for compile}
|
||||
{--s|silent : Enables silent mode, no output will be shown.}
|
||||
{--d|disable-tty : Disable tty mode}
|
||||
{--no-progress : Do not show mix progress}';
|
||||
|
||||
/**
|
||||
* @var string The console command description.
|
||||
*/
|
||||
protected $description = 'Vite and compile assets on-the-fly as changes are made.';
|
||||
|
||||
/**
|
||||
* @var array List of commands that this command replaces (aliases)
|
||||
*/
|
||||
protected $replaces = [
|
||||
'vite:dev'
|
||||
];
|
||||
|
||||
/**
|
||||
* Call the AssetCompile::watchHandle with the vite type
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
return $this->watchHandle('vite');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the command array to create a Process object with
|
||||
*/
|
||||
protected function createCommand(string $configPath): array
|
||||
{
|
||||
$command = parent::createCommand($configPath);
|
||||
$key = array_search('build', $command);
|
||||
unset($command[$key]);
|
||||
|
||||
$command[] = '--host';
|
||||
|
||||
return array_values($command);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the cleanup of this command if a termination signal is received
|
||||
*/
|
||||
public function handleCleanup(): void
|
||||
{
|
||||
$this->newLine();
|
||||
$this->info('Running compile to ensure files exist after exit');
|
||||
|
||||
$this->call('vite:compile', [
|
||||
'--package' => $this->argument('package'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user