feat: VivesPOS landing on Winter CMS 1.2 — theme + plugin + Dockerfile
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:
2026-08-21 19:29:00 -06:00
commit 1f72193a64
3266 changed files with 531480 additions and 0 deletions

View File

@@ -0,0 +1,297 @@
<?php
namespace System\Classes\Asset;
use Closure;
use Winter\Storm\Support\Traits\Singleton;
/**
* Asset Bundle manager.
*
* This class manages "asset bundles" registered by the core and plugins that are used by the
* [mix|vite]:create commands to generate & populate the required files for a given bundle.
* Bundles include information on the specific packages & versions required for the bundle
* to function in the context of the Winter package (plugin or theme) it is being used in,
* as well as dependencies specific to the desired compiler (e.g. mix or vite).
*
* @package winter\wn-system-module
* @author Jack Wilkinson <me@jackwilky.com>
* @copyright Winter CMS Maintainers
*/
class BundleManager
{
use Singleton;
protected const HANDLER_SETUP = '_setup';
protected const HANDLER_SCAFFOLD = '_scaffold';
/**
* List of packages available to install. Allows for `$compilerName` => [`CompilerSpecificPackage`]
*/
protected array $defaultPackages = [
'tailwind' => [
'tailwindcss' => '^3.4.0',
'@tailwindcss/forms' => '^0.5.3',
'@tailwindcss/typography' => '^0.5.2',
],
'vue' => [
'vue' => '^3.4.0',
'vite' => [
'@vitejs/plugin-vue' => '^5.0.5'
],
],
'react' => [
'react' => '^19.0.0',
'react-dom' => '^19.0.0',
'vite' => [
'@vitejs/plugin-react' => '^4.3.4',
],
],
];
/**
* List of registered asset bundles in the system
*/
protected array $registeredBundles = [];
/**
* Initialize the singleton
*/
public function init(): void
{
// Register the default bundles
$this->registerCallback(function (self $manager) {
$manager->registerBundles($this->defaultPackages);
$manager->registerSetupHandler('tailwind', function (string $packagePath, string $packageType) {
$this->writeFile(
$packagePath . '/tailwind.config.js',
$this->getFixture('tailwind/tailwind.' . $packageType . '.config.js.fixture')
);
$this->writeFile(
$packagePath . '/postcss.config.mjs',
$this->getFixture('tailwind/postcss.config.js.fixture')
);
});
$manager->registerSetupHandler('react', function (string $packagePath, string $packageType) use ($manager) {
if ($this->option('no-stubs')) {
return;
}
$this->writeFile(
$packagePath . '/assets/src/js/components/App.jsx',
$this->getFixture('react/App.jsx.fixture')
);
$this->writeFile(
$packagePath . '/assets/src/js/' . strtolower($this->argument('packageName')) . '.jsx',
$this->getFixture('react/package.jsx.fixture')
);
});
$manager->registerScaffoldHandler('tailwind', function (string $contents, string $contentType) {
return match ($contentType) {
'mix' => $contents . PHP_EOL . <<<JAVASCRIPT
mix.postCss('assets/src/css/{{packageName}}.css', 'assets/dist/css/{{packageName}}.css', [
require('postcss-import'),
require('tailwindcss'),
require('autoprefixer'),
]);
JAVASCRIPT,
'css' => $this->getFixture('css/tailwind.css.fixture'),
default => $contents
};
});
$manager->registerScaffoldHandler('vue', function (string $contents, string $contentType) {
return match ($contentType) {
'vite' => str_replace(
'}),',
<<<JAVASCRIPT
}),
vue({
template: {
transformAssetUrls: {
// The Vue plugin will re-write asset URLs, when referenced
// in Single File Components, to point to the Laravel web
// server. Setting this to `null` allows the Laravel plugin
// to instead re-write asset URLs to point to the Vite
// server instead.
base: null,
// The Vue plugin will parse absolute URLs and treat them
// as absolute paths to files on disk. Setting this to
// `false` will leave absolute URLs un-touched so they can
// reference assets in the public directory as expected.
includeAbsolute: false,
},
},
}),
JAVASCRIPT,
str_replace(
'import laravel from \'laravel-vite-plugin\';',
'import laravel from \'laravel-vite-plugin\';' . PHP_EOL . 'import vue from \'@vitejs/plugin-vue\';',
$contents
)
),
'mix' => str_replace(
'mix.js(\'assets/src/js/{{packageName}}.js\', \'assets/dist/js/{{packageName}}.js\');',
'mix.js(\'assets/src/js/{{packageName}}.js\', \'assets/dist/js/{{packageName}}.js\').vue({ version: 3 });',
$contents
),
'js' => $this->getFixture('js/vue.js.fixture'),
default => $contents
};
});
$manager->registerScaffoldHandler('react', function (string $contents, string $contentType) {
return match ($contentType) {
'vite' => str_replace(
'}),',
<<<JAVASCRIPT
}),
react(),
JAVASCRIPT,
str_replace(
'import laravel from \'laravel-vite-plugin\';',
'import laravel from \'laravel-vite-plugin\';' . PHP_EOL . 'import react from \'@vitejs/plugin-react\';',
$contents
)
),
'mix' => str_replace(
'mix.js(\'assets/src/js/{{packageName}}.js\', \'assets/dist/js/{{packageName}}.js\');',
'mix.js(\'assets/src/js/{{packageName}}.js\', \'assets/dist/js/{{packageName}}.js\').react();',
$contents
),
'js' => str_replace(
'{{packageName}}',
strtolower($this->argument('packageName')),
$this->getFixture('react/package.js.fixture')
),
default => $contents
};
});
});
}
/**
* Returns a list of the registered asset bundles.
*/
public function listRegisteredBundles(): array
{
return $this->registeredBundles;
}
/**
* Get all bundles configured
*/
public function getBundles(): array
{
return array_keys($this->listRegisteredBundles());
}
/**
* Get the packages for a bundle, with compiler specific packages
*/
public function getBundlePackages(string $name, string $assetType): array
{
$config = $this->listRegisteredBundles()[$name] ?? [];
$packages = [];
foreach ($config as $key => $value) {
// Skip handlers
if (in_array($key, [static::HANDLER_SETUP, static::HANDLER_SCAFFOLD])) {
continue;
}
// Merge in any compiler specific packages for the current compiler
if (is_array($value)) {
if ($key === $assetType) {
$packages = array_merge($packages, $value);
}
continue;
}
$packages[$key] = $value;
}
return $packages;
}
/**
* Registers a callback function that defines asset bundles. The callback function
* should register bundles by calling the manager's registerBundles() function.
* This instance is passed to the callback function as an argument. Usage:
*
* BundleManager::registerCallback(function ($manager) {
* $manager->registerAssetBundles([...]);
* });
*
*/
public function registerCallback(callable $callback): static
{
$callback($this);
return $this;
}
/**
* Registers asset bundles.
*/
public function registerBundles(array $definitions): static
{
foreach ($definitions as $name => $definition) {
$this->registerBundle($name, $definition);
}
return $this;
}
/**
* Registers a single asset bundle.
*/
public function registerBundle(string $name, array $definition): static
{
$this->registeredBundles[$name] = $definition;
return $this;
}
/**
* Registers a single bundle setup handler.
*/
public function registerSetupHandler(string $name, Closure $closure): static
{
$this->registeredBundles[$name][static::HANDLER_SETUP] = $closure;
return $this;
}
/**
* Registers a single bundle scaffold handler.
*/
public function registerScaffoldHandler(string $name, Closure $closure): static
{
$this->registeredBundles[$name][static::HANDLER_SCAFFOLD] = $closure;
return $this;
}
/**
* Gets the setup handler for a bundle.
*/
public function getSetupHandler(string $name): ?Closure
{
return $this->listRegisteredBundles()[$name][static::HANDLER_SETUP] ?? null;
}
/**
* Gets the scaffold handler for a bundle.
*/
public function getScaffoldHandler(string $name): ?Closure
{
return $this->listRegisteredBundles()[$name][static::HANDLER_SCAFFOLD] ?? null;
}
}

View File

@@ -0,0 +1,278 @@
<?php
namespace System\Classes\Asset;
use InvalidArgumentException;
use RuntimeException;
use Winter\Storm\Support\Facades\File;
/**
* PHP based interface for interacting with package.json files. This allows for modification of deps, devDeps, package
* name and workspaces.
*
* @package winter\wn-system-module
* @author Jack Wilkinson <me@jackwilky.com>
* @author Winter CMS
*/
class PackageJson
{
/**
* The contents of the package.json being modified
*/
protected array $data = [];
/**
* Create a new instance with optional path, loads file if file already exists
* @throws \JsonException
*/
public function __construct(
protected ?string $path = null
) {
if (File::exists($this->path)) {
// Test the json to insure it's valid
$json = json_decode(File::get($this->path), JSON_OBJECT_AS_ARRAY);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new \JsonException('The contents of the file "' . $this->path . '" is not valid json.');
}
$this->data = $json;
}
}
/**
* Returns the package name if set
*/
public function getName(): ?string
{
return $this->data['name'] ?? null;
}
/**
* Sets the package name, throws `InvalidArgumentException` on invalid name
*/
public function setName(?string $name): static
{
if (is_null($name)) {
unset($this->data['name']);
return $this;
}
if ($name !== strtolower($name)) {
throw new InvalidArgumentException('Package names must be lower case');
}
if (preg_match('/^([._])/', $name)) {
throw new InvalidArgumentException('Package names must not start with . or _');
}
if (preg_match('/[~\'\"!()*]/', $name)) {
throw new InvalidArgumentException('Package names must not include special characters');
}
if (strlen($name) > 214) {
throw new InvalidArgumentException('Package names must not be longer than 214 characters');
}
if ($name !== trim($name)) {
throw new InvalidArgumentException('Package names must not include whitespace');
}
$this->data['name'] = $name;
return $this;
}
/**
* Checks if workspace package is set
*/
public function hasWorkspace(string $path): bool
{
return in_array($path, $this->data['workspaces']['packages'] ?? []);
}
/**
* Adds a new workspace, removes from ignored workspaces if present
*/
public function addWorkspace(string $path): static
{
if (!in_array($path, $this->data['workspaces']['packages'] ?? [])) {
$this->data['workspaces']['packages'][] = $path;
}
if (($key = array_search($path, $this->data['workspaces']['ignoredPackages'] ?? [])) !== false) {
// remove the package from ignored workspaces
unset($this->data['workspaces']['ignoredPackages'][$key]);
// reset keys
$this->data['workspaces']['ignoredPackages'] = array_values($this->data['workspaces']['ignoredPackages']);
}
// Sort the packages
asort($this->data['workspaces']['packages']);
$this->data['workspaces']['packages'] = array_values($this->data['workspaces']['packages']);
return $this;
}
/**
* Removes a workspace
*/
public function removeWorkspace(string $path): static
{
if (($key = array_search($path, $this->data['workspaces']['packages'] ?? [])) !== false) {
// remove the package from workspace packages
unset($this->data['workspaces']['packages'][$key]);
// reset keys
$this->data['workspaces']['packages'] = array_values($this->data['workspaces']['packages']);
}
return $this;
}
/**
* Check if package is ignored
*/
public function hasIgnoredPackage(string $path): bool
{
return in_array($path, $this->data['workspaces']['ignoredPackages'] ?? []);
}
/**
* Adds an ignored package, removes from workspaces if present
*/
public function addIgnoredPackage(string $path): static
{
if (!in_array($path, $this->data['workspaces']['ignoredPackages'] ?? [])) {
$this->data['workspaces']['ignoredPackages'][] = $path;
}
if (($key = array_search($path, $this->data['workspaces']['packages'] ?? [])) !== false) {
// remove the package from ignored workspaces
unset($this->data['workspaces']['packages'][$key]);
// reset keys
$this->data['workspaces']['packages'] = array_values($this->data['workspaces']['packages']);
}
// Sort the packages
asort($this->data['workspaces']['ignoredPackages']);
$this->data['workspaces']['ignoredPackages'] = array_values($this->data['workspaces']['ignoredPackages'] ?? []);
return $this;
}
/**
* Removes an ignored package
*/
public function removeIgnoredPackage(string $path): static
{
if (($key = array_search($path, $this->data['workspaces']['ignoredPackages'] ?? [])) !== false) {
// remove the package from workspace packages
unset($this->data['workspaces']['ignoredPackages'][$key]);
// reset keys
$this->data['workspaces']['ignoredPackages'] = array_values($this->data['workspaces']['ignoredPackages']);
}
return $this;
}
/**
* Checks if package.json has a dependency
*/
public function hasDependency(string $package): bool
{
return isset($this->data['dependencies'][$package]) || isset($this->data['devDependencies'][$package]);
}
/**
* Adds a dependency, supports adding to `dependencies` or `devDependencies` based on `$dev` and allows moving if
* `$overwrite` is set
*/
public function addDependency(string $package, string $version, bool $dev = false, bool $overwrite = false): static
{
// If the dep is defined already, but we are not overwriting, then exit
if (
(isset($this->data['dependencies'][$package]) || isset($this->data['devDependencies'][$package]))
&& !$overwrite
) {
return $this;
}
// Clear any existing settings because we are overwriting
$this->removeDependency($package);
// Define the dep
$this->data[$dev ? 'devDependencies' : 'dependencies'][$package] = $version;
return $this;
}
/**
* Removes a package from both `dependencies` and `devDependencies`
*/
public function removeDependency(string $package): static
{
unset($this->data['dependencies'][$package], $this->data['devDependencies'][$package]);
return $this;
}
/**
* Returns if a script exists
*/
public function hasScript(string $name): bool
{
return isset($this->data['scripts'][$name]);
}
/**
* Returns the value of a script by name
*/
public function getScript(string $name): ?string
{
return $this->data['scripts'][$name] ?? null;
}
/**
* Adds a script
*/
public function addScript(string $name, string $script): static
{
$this->data['scripts'][$name] = $script;
return $this;
}
/**
* Removes a script by name
*/
public function removeScript(string $name): static
{
unset($this->data['scripts'][$name]);
return $this;
}
/**
* Returns the package.json contents as an array
*/
public function getContents(): array
{
return $this->data;
}
/**
* Returns the path of the package.json if set
*/
public function getPath(): ?string
{
return $this->path;
}
/**
* Saves the contents to a file, if the object was init'ed with a path it will save to the path, or can be
* overwritten with `$path`.
*/
public function save(?string $path = null): int
{
return File::put(
$path ?? $this->path ?? throw new RuntimeException('Unable to save, no path given'),
json_encode($this->data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)
);
}
}

View File

@@ -0,0 +1,414 @@
<?php
namespace System\Classes\Asset;
use Cms\Classes\Theme;
use InvalidArgumentException;
use System\Classes\PluginManager;
use Winter\Storm\Exception\SystemException;
use Winter\Storm\Filesystem\PathResolver;
use Winter\Storm\Support\Facades\Config;
use Winter\Storm\Support\Facades\File;
use Winter\Storm\Support\Str;
/**
* Package manager.
*
* This class manages compilable asset "packages" registered by modules, plugins, and themes that
* provide configurations for Node.js based compilers (e.g. mix or vite) to process.
*
* @package winter\wn-system-module
* @author Jack Wilkinson <me@jackwilky.com>
* @copyright Winter CMS Maintainers
*/
class PackageManager
{
use \Winter\Storm\Support\Traits\Singleton;
public const TYPE_THEME = 'theme';
public const TYPE_MODULE = 'module';
public const TYPE_PLUGIN = 'plugin';
/**
* The filename that stores the package definition.
*/
protected PackageJson $packageJson;
/**
* @var array<string, array<string, string>> List of package types and registration methods
*/
protected array $compilableConfigs = [
'mix' => [
'configFile' => 'winter.mix.js'
],
'vite' => [
'configFile' => 'vite.config.mjs'
]
];
/**
* A list of packages registered for compiling.
*/
protected array $packages = [];
/**
* Registered callbacks.
*/
protected static array $callbacks = [];
/**
* Constructor.
*/
public function init(): void
{
$this->setPackageJsonPath(base_path('package.json'));
$packagePaths = [];
/*
* Get packages registered in plugins.
*
* In the Plugin.php file for your plugin, you can define the `registerMixPackages` or `registerVitePackages`
* method and return an array, with the name of the package being the key, and the build config path - relative
* to the plugin directory - as the value.
*
* Example:
*
* public function registerMixPackages(): array
* {
* return [
* 'package-name-1' => 'winter.mix.js',
* 'package-name-2' => 'assets/js/build.js',
* ];
* }
*
* public function registerVitePackages(): array
* {
* return [
* 'package-name-1' => 'vite.config.mjs',
* 'package-name-2' => 'assets/js/build.js',
* ];
* }
*/
foreach ($this->compilableConfigs as $type => $config) {
$packages = PluginManager::instance()->getRegistrationMethodValues(
$this->getRegistrationMethod($type)
);
if (count($packages)) {
foreach ($packages as $pluginCode => $packageArray) {
if (!is_array($packageArray)) {
continue;
}
foreach ($packageArray as $name => $package) {
$this->registerPackage(
$name,
PluginManager::instance()->getPluginPath($pluginCode) . '/' . $package,
$type
);
}
}
}
// Get the currently enabled modules
$enabledModules = Config::get('cms.loadModules', []);
if (in_array('Cms', $enabledModules)) {
// Allow current theme to define mix assets
$theme = Theme::getActiveTheme();
if (!is_null($theme)) {
$mix = $theme->getConfigValue($type, []);
if (count($mix)) {
foreach ($mix as $name => $file) {
$this->registerPackage($name, $theme->getPath() . '/' . $file, $type);
}
}
}
}
// Search modules for compilable packages to autoregister
foreach ($enabledModules as $module) {
$module = strtolower($module);
$path = base_path('modules' . DIRECTORY_SEPARATOR . $module) . DIRECTORY_SEPARATOR . $config['configFile'];
if (File::exists($path)) {
$packagePaths[$type]["module-$module"] = $path;
}
}
// Search plugins for compilable packages to autoregister
$plugins = PluginManager::instance()->getPlugins();
foreach ($plugins as $plugin) {
$path = $plugin->getPluginPath() . '/' . $config['configFile'];
if (File::exists($path)) {
$packagePaths[$type][$plugin->getPluginIdentifier()] = $path;
}
}
// Search themes for compilable packages to autoregister
if (in_array('Cms', $enabledModules)) {
$themes = Theme::all();
foreach ($themes as $theme) {
$path = $theme->getPath() . '/' . $config['configFile'];
if (File::exists($path)) {
$packagePaths[$type]["theme-" . $theme->getId()] = $path;
}
}
}
}
// Register the autodiscovered compilable packages
foreach ($packagePaths as $type => $packages) {
foreach ($packages as $package => $path) {
try {
$this->registerPackage($package, $path, $type);
} catch (SystemException $e) {
// Either the package name or the config file path have already been registered, skip.
continue;
}
}
}
}
/**
* Register a compilable config.
*/
public function registerCompilable(string $name, array $config): void
{
$this->compilableConfigs[$name] = $config;
}
/**
* Registers a callback for processing.
*/
public static function registerCallback(callable $callback): void
{
static::$callbacks[] = $callback;
}
/**
* Calls the deferred callbacks.
*/
public function fireCallbacks(): static
{
// Call callbacks
foreach (static::$callbacks as $callback) {
$callback($this);
}
return $this;
}
/**
* Returns the count of packages registered.
*/
public function getPackageCount(): int
{
return array_sum(array_map(fn ($packages) => count($packages), ...$this->packages));
}
/**
* Returns all packages registered.
*/
public function getPackages(string $type, bool $includeIgnored = false): array
{
$packages = $this->packages[$type] ?? [];
foreach ($packages as $index => $package) {
$packages[$index]['ignored'] = $this->isPackageIgnored($package['path']);
}
ksort($packages);
if (!$includeIgnored) {
return array_filter($packages, function ($package) {
return !($package['ignored'] ?? false);
});
}
return $packages;
}
/**
* Returns if package(s) is registered.
*/
public function hasPackage(string $name, bool $includeIgnored = false): bool
{
foreach ($this->packages ?? [] as $packages) {
foreach ($packages as $packageName => $package) {
if ($name === $packageName) {
if ((!$this->isPackageIgnored($package['path']) || $includeIgnored)) {
return true;
}
return false;
}
}
}
return false;
}
/**
* Returns package(s).
*/
public function getPackage(string $name, bool $includeIgnored = false): array
{
$results = [];
foreach ($this->packages ?? [] as $type => $packages) {
foreach ($packages as $packageName => $package) {
if (($name === $packageName)) {
if (!$this->isPackageIgnored($package['path']) || $includeIgnored) {
$results[] = $package + ['type' => $type];
}
}
}
}
return $results;
}
/**
* Registers an entity as a package for compilation.
*
* Entities can include plugins, components, themes, modules and much more.
*
* The name of the package is an alias that can be used to reference this package in other methods within this
* class.
*
* By default, the `PackageManager` class will look for a `package.json` file for Node dependencies, and a config
* file for the compilable configuration
*
* @param string $name The name of the package being registered
* @param string $path The path to the compilable JS configuration file (it must be inside of the base_path()). If there is a related package.json file
* then it is required to be present in the same directory as the config file
* @param string $type The type of compilable
* @throws SystemException
*/
public function registerPackage(string $name, string $path, string $type = 'mix'): void
{
// Symbolize the path
$path = File::symbolizePath($path);
// Normalize the arguments
$name = strtolower($name);
$resolvedPath = PathResolver::resolve($path);
$pinfo = pathinfo($resolvedPath);
$relativePath = Str::after($pinfo['dirname'], base_path() . DIRECTORY_SEPARATOR);
$configFile = $pinfo['basename'];
// Require $configFile to be a JS file
$extension = File::extension($configFile);
if (!in_array($extension, ['js', 'mjs'])) {
throw new SystemException(sprintf(
'Compilable configuration for package "%s" must be a JavaScript file ending with .js or .mjs',
$name
));
}
// Check that the package path exists
if (!File::exists(base_path($relativePath))) {
throw new InvalidArgumentException(sprintf(
'Cannot register "%s" as a compilable package; the "%s" path does not exist.',
$name,
base_path($relativePath)
));
}
$package = $relativePath . '/package.json';
$config = $relativePath . DIRECTORY_SEPARATOR . $configFile;
if (!File::exists(base_path($config))) {
throw new SystemException(sprintf(
'Cannot register "%s" as a compilable package; the config file "%s" does not exist.',
$name,
$config
));
}
// Check for any existing packages already registered under the provided name
if (isset($this->packages[$name])) {
throw new SystemException(sprintf(
'Cannot register "%s" as a compilable package; it has already been registered at %s.',
$name,
$this->packages[$name]['config']
));
}
// Check for any existing package that already registers the given compilable config path
foreach ($this->packages[$type] ?? [] as $packageName => $settings) {
if ($settings['config'] === $config) {
// If the package name is the same, we'll just discard the repeated registration
if ($packageName === $name) {
return;
}
throw new SystemException(sprintf(
'Cannot register "%s" (%s) as a compilable package; it has already been registered as %s.',
$name,
$config,
$packageName
));
}
}
// Register the package
$this->packages[$type][$name] = [
'path' => $relativePath,
'package' => $package,
'config' => $config
];
}
/**
* Returns an expected package type from its name
*/
public function getPackageTypeFromName(string $package): ?string
{
// Check if package could be a module
if (Str::startsWith($package, 'module-') && !in_array($package, ['system', 'backend', 'cms'])) {
return static::TYPE_MODULE;
}
// Check if package could be a theme
if (
in_array('Cms', Config::get('cms.loadModules'))
&& Str::startsWith($package, 'theme-')
&& Theme::exists(Str::after($package, 'theme-'))
) {
return static::TYPE_THEME;
}
// Check if a package could be a plugin
if (PluginManager::instance()->exists($package)) {
return static::TYPE_PLUGIN;
}
return null;
}
/**
* Set the package.json file path used for checking if packages are in workspaces or ignored
*/
public function setPackageJsonPath(string $packageJsonPath): static
{
$this->packageJson = new PackageJson($packageJsonPath);
return $this;
}
/**
* Returns the registration method for a compiler type
*/
protected function getRegistrationMethod(string $type): string
{
return sprintf('register%sPackages', ucfirst($type));
}
/**
* Check if the provided package is ignored.
*/
protected function isPackageIgnored(string $packagePath): bool
{
return $this->packageJson->hasIgnoredPackage($packagePath);
}
}

View File

@@ -0,0 +1,80 @@
<?php
namespace System\Classes\Asset;
use Illuminate\Foundation\Vite as LaravelVite;
use Illuminate\Support\Facades\App;
use Illuminate\Support\HtmlString;
use Winter\Storm\Exception\SystemException;
class Vite extends LaravelVite
{
/**
* Generate Vite tags for an entrypoint(s).
*
* @param string|array $entrypoints The list of entry points for Vite
* @param string|null $package The package name of the plugin or theme
* @param string|null $buildDirectory The Vite build directory
*
* @return HtmlString
*
* @throws SystemException
*/
public function __invoke($entrypoints, $package = null, ?string $buildDirectory = null)
{
if (!$package) {
throw new \InvalidArgumentException('A package must be passed');
}
$compilableAssetPackage = static::resolvePackage($package);
$this->useHotFile(base_path($compilableAssetPackage['path'] . '/assets/dist/hot'));
return parent::__invoke($entrypoints, $compilableAssetPackage['path'] . ($buildDirectory ?? '/assets/dist'));
}
/**
* @throws SystemException if the package could not be found
*/
protected static function resolvePackage(string $package): array
{
// Normalise the package name
$package = strtolower($package);
if (!($compilableAssetPackage = PackageManager::instance()->getPackages('vite', true)[$package] ?? null)) {
throw new SystemException('Unable to resolve package: ' . $package);
}
return $compilableAssetPackage;
}
/**
* Helper method to generate Vite tags for an entrypoint(s).
*
* @param string|array $entrypoints The list of entry points for Vite
* @param string $package The package name of the plugin or theme
* @param string|null $buildDirectory The Vite build directory
*
* @throws SystemException
*/
public static function tags(array|string $entrypoints, string $package, ?string $buildDirectory = null): HtmlString
{
return App::make(\Illuminate\Foundation\Vite::class)($entrypoints, $package, $buildDirectory);
}
/**
* Helper method to generate Vite React Refresh tag.
*
* @param string $package The package name of the plugin or theme
* @param string|null $buildDirectory The Vite build directory
*
* @throws SystemException
*/
public static function reactRefreshTag(string $package, ?string $buildDirectory = null): ?HtmlString
{
$compilableAssetPackage = static::resolvePackage($package);
return App::make(\Illuminate\Foundation\Vite::class)
->useHotFile(base_path($compilableAssetPackage['path'] . '/assets/dist/hot'))
->useBuildDirectory($compilableAssetPackage['path'] . ($buildDirectory ?? '/assets/dist'))
->reactRefresh();
}
}