mirror of
https://github.com/wintercms/winter.git
synced 2024-06-28 05:33:29 +02:00
269 lines
8.7 KiB
PHP
269 lines
8.7 KiB
PHP
<?php namespace System\Classes;
|
|
|
|
use File;
|
|
use Config;
|
|
use SystemException;
|
|
use Cms\Classes\Theme;
|
|
use Winter\Storm\Support\Str;
|
|
use System\Classes\PluginManager;
|
|
use Winter\Storm\Filesystem\PathResolver;
|
|
|
|
/**
|
|
* Mix assets using Laravel Mix for Node.js compilation and processing.
|
|
*
|
|
* This works similar to the `System\Classes\CombineAssets` class in that it allows modules, plugins and themes to
|
|
* register configurations that will be passed on to Laravel Mix and Node.js for compilation and processing.
|
|
*
|
|
* @package winter\wn-system-module
|
|
* @author Ben Thomson <git@alfreido.com>, Jack Wilkinson <jax@jaxwilko.com>
|
|
* @author Winter CMS
|
|
*/
|
|
class MixAssets
|
|
{
|
|
use \Winter\Storm\Support\Traits\Singleton;
|
|
|
|
/**
|
|
* The filename that stores the package definition.
|
|
*/
|
|
protected string $packageJson = 'package.json';
|
|
|
|
/**
|
|
* The filename that stores the Laravel Mix configuration
|
|
*/
|
|
protected string $mixJs = 'winter.mix.js';
|
|
|
|
/**
|
|
* A list of packages registered for mixing.
|
|
*/
|
|
protected array $packages = [];
|
|
|
|
/**
|
|
* Registered callbacks.
|
|
*/
|
|
protected static array $callbacks = [];
|
|
|
|
/**
|
|
* Constructor.
|
|
*/
|
|
public function init(): void
|
|
{
|
|
/*
|
|
* Get packages registered in plugins.
|
|
*
|
|
* In the Plugin.php file for your plugin, you can define the "registerMixPackages" 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()
|
|
* {
|
|
* return [
|
|
* 'package-name-1' => 'winter.mix.js',
|
|
* 'package-name-2' => 'assets/js/build.js',
|
|
* ];
|
|
* }
|
|
*/
|
|
$packages = PluginManager::instance()->getRegistrationMethodValues('registerMixPackages');
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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('mix', []);
|
|
|
|
if (count($mix)) {
|
|
foreach ($mix as $name => $file) {
|
|
$this->registerPackage($name, $theme->getPath() . '/' . $file);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$packagePaths = [];
|
|
|
|
// Search modules for Mix packages to autoregister
|
|
foreach ($enabledModules as $module) {
|
|
$module = strtolower($module);
|
|
$path = base_path('modules' . DIRECTORY_SEPARATOR . $module) . DIRECTORY_SEPARATOR . $this->mixJs;
|
|
if (File::exists($path)) {
|
|
$packagePaths["module-$module"] = $path;
|
|
}
|
|
}
|
|
|
|
// Search plugins for Mix packages to autoregister
|
|
$plugins = PluginManager::instance()->getPlugins();
|
|
foreach ($plugins as $plugin) {
|
|
$path = $plugin->getPluginPath() . '/' . $this->mixJs;
|
|
if (File::exists($path)) {
|
|
$packagePaths[$plugin->getPluginIdentifier()] = $path;
|
|
}
|
|
}
|
|
|
|
// Search themes for Mix packages to autoregister
|
|
if (in_array('Cms', $enabledModules)) {
|
|
$themes = Theme::all();
|
|
foreach ($themes as $theme) {
|
|
$path = $theme->getPath() . '/' . $this->mixJs;
|
|
if (File::exists($path)) {
|
|
$packagePaths["theme-" . $theme->getId()] = $path;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Register the autodiscovered Mix packages
|
|
foreach ($packagePaths as $package => $path) {
|
|
try {
|
|
$this->registerPackage($package, $path);
|
|
} catch (SystemException $e) {
|
|
// Either the package name or the mixJs path have already been registered, skip.
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Registers a callback for processing.
|
|
*/
|
|
public static function registerCallback(callable $callback): void
|
|
{
|
|
static::$callbacks[] = $callback;
|
|
}
|
|
|
|
/**
|
|
* Calls the deferred callbacks.
|
|
*/
|
|
public function fireCallbacks(): void
|
|
{
|
|
// Call callbacks
|
|
foreach (static::$callbacks as $callback) {
|
|
$callback($this);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the count of packages registered.
|
|
*/
|
|
public function getPackageCount(): int
|
|
{
|
|
return count($this->packages);
|
|
}
|
|
|
|
/**
|
|
* Returns all packages registered.
|
|
*/
|
|
public function getPackages(bool $includeIgnored = false): array
|
|
{
|
|
ksort($this->packages);
|
|
|
|
if (!$includeIgnored) {
|
|
return array_filter($this->packages, function ($package) {
|
|
return !($package['ignored'] ?? false);
|
|
});
|
|
}
|
|
|
|
return $this->packages;
|
|
}
|
|
|
|
/**
|
|
* Registers an entity as a package for mixing.
|
|
*
|
|
* 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 MixAssets class will look for a `package.json` file for Node dependencies, and a `winter.mix.js`
|
|
* file for the Laravel Mix configuration
|
|
*
|
|
* @param string $name The name of the package being registered
|
|
* @param string $path The path to the Mix JS configuration file. If there is a related package.json file then it is
|
|
* required to be present in the same directory as the winter.mix.js file
|
|
*/
|
|
public function registerPackage(string $name, string $path): void
|
|
{
|
|
// Symbolize the path
|
|
$path = File::symbolizePath($path);
|
|
|
|
// Normalize the arguments
|
|
$name = strtolower($name);
|
|
$resolvedPath = PathResolver::resolve($path);
|
|
$pinfo = pathinfo($resolvedPath);
|
|
$path = Str::after($pinfo['dirname'], base_path() . DIRECTORY_SEPARATOR);
|
|
$mixJs = $pinfo['basename'];
|
|
|
|
// Require $mixJs to be a JS file
|
|
$extension = File::extension($mixJs);
|
|
if ($extension !== 'js') {
|
|
throw new SystemException(
|
|
sprintf('The mix configuration for package "%s" must be a JavaScript file ending with .js', $name)
|
|
);
|
|
}
|
|
|
|
// Check that the package path exists
|
|
if (!File::exists($path)) {
|
|
throw new SystemException(
|
|
sprintf('Cannot register "%s" as a Mix package; the "%s" path does not exist.', $name, $path)
|
|
);
|
|
}
|
|
|
|
// 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 Mix package; it has already been registered at %s.', $name, $this->packages[$name]['mix'])
|
|
);
|
|
}
|
|
|
|
$package = "$path/{$this->packageJson}";
|
|
$mix = $path . DIRECTORY_SEPARATOR . $mixJs;
|
|
|
|
// Check for any existing package that already registers the given Mix path
|
|
foreach ($this->packages as $packageName => $config) {
|
|
if ($config['mix'] === $mix) {
|
|
throw new SystemException(
|
|
sprintf('Cannot register "%s" (%s) as a Mix package; it has already been registered as %s.', $name, $mix, $packageName)
|
|
);
|
|
}
|
|
}
|
|
|
|
// Register the package
|
|
$this->packages[$name] = [
|
|
'path' => $path,
|
|
'package' => $package,
|
|
'mix' => $mix,
|
|
'ignored' => $this->isPackageIgnored($path),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Check if the provided package is ignored.
|
|
*/
|
|
protected function isPackageIgnored(string $packagePath): bool
|
|
{
|
|
// Load the main package.json for the project
|
|
$packageJsonPath = base_path($this->packageJson);
|
|
$packageJson = [];
|
|
if (File::exists($packageJsonPath)) {
|
|
$packageJson = json_decode(File::get($packageJsonPath), true);
|
|
}
|
|
$included = $packageJson['workspaces']['packages'] ?? [];
|
|
$ignored = $packageJson['workspaces']['ignoredPackages'] ?? [];
|
|
return in_array($packagePath, $ignored);
|
|
}
|
|
}
|