Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add asset BundleManager & re-organize the asset helper classes #1164

Merged
merged 51 commits into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from 43 commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
c68f339
Added a manager to handle all package versions
jaxwilko Jul 17, 2024
c9b9054
Removed versions from install commands
jaxwilko Jul 17, 2024
5f1bf82
Removed versions, added support for compiler specific packages and ad…
jaxwilko Jul 17, 2024
e611647
Swapped version usage for new version manager
jaxwilko Jul 17, 2024
3a3b60f
Added tests for NodePackageVersions
jaxwilko Jul 17, 2024
a852a3b
Added comments to tests
jaxwilko Jul 17, 2024
b3e2f1c
Added fix for incorrect namespace casing
jaxwilko Jul 17, 2024
4f1ac3e
Added fix for incorrect namespace casing and removed unrequired import
jaxwilko Jul 17, 2024
6f249cd
Added getPath() to PackageJson
jaxwilko Jul 17, 2024
d2bb53d
Reworked NodePackageVersions into NodePackages
jaxwilko Jul 17, 2024
b33071b
Migrated commands to use new NodePackages class
jaxwilko Jul 17, 2024
79d1ff2
Renamed / removed redundent fixtures
jaxwilko Jul 17, 2024
cb30d38
Added config / stub generation via helpers
jaxwilko Jul 17, 2024
560dbf9
Added fix for test
jaxwilko Jul 17, 2024
c4ab3f0
Reordered handlers
jaxwilko Jul 17, 2024
2f54512
Added fix for vite vue & switch to match stmts
jaxwilko Jul 17, 2024
ab57983
Code clean up
jaxwilko Jul 17, 2024
541a951
Removed import
jaxwilko Jul 18, 2024
f119347
Added skip test when bin is not present
jaxwilko Jul 18, 2024
716e06c
Renamed vars to be more logical
jaxwilko Jul 18, 2024
9b44568
Added code clean up
jaxwilko Jul 18, 2024
21f2474
Renamed config for handlers
jaxwilko Jul 18, 2024
f44803e
Added config loader for scaffold handlers
jaxwilko Jul 18, 2024
b37a17a
Added default to asset dir for vite assets
jaxwilko Jul 19, 2024
3875c32
Added stubs by default and added --no-stubs to disable stubbing
jaxwilko Jul 19, 2024
b8e36fb
Use correct verb for reporting on changes to package.json
LukeTowers Jul 19, 2024
ffaa75d
Added fixes for tests
jaxwilko Jul 19, 2024
7624315
Rename / reorganize classes
LukeTowers Jul 19, 2024
f32b3ca
Fix reference to *:create commands
LukeTowers Jul 19, 2024
b618bfd
Added fixes for PackageJsonTest
jaxwilko Jul 20, 2024
8e0240a
Move compiler required packages to the compiler installer commands
LukeTowers Jul 21, 2024
ef2987d
Rewrite the BundleManager to match the Manager class pattern used els…
LukeTowers Jul 21, 2024
3ca4bc2
Cleanup
LukeTowers Jul 21, 2024
122cbf5
Remove config from bundlemanager test
LukeTowers Jul 21, 2024
5248458
Fix outdated property ref
LukeTowers Jul 21, 2024
65736c7
Fix check for unitialized bundle manager
LukeTowers Jul 21, 2024
42bdebd
Fix BundleManager tests
LukeTowers Jul 21, 2024
7966c37
Added type hint
jaxwilko Jul 21, 2024
5413061
Added test cases
jaxwilko Jul 21, 2024
fe5df7e
Added test for overriding a default bundle
jaxwilko Jul 21, 2024
4591531
Added helper method and afterExecution support
jaxwilko Jul 21, 2024
a3a76b8
Added after config create info with vite twig code
jaxwilko Jul 21, 2024
3b9abac
Added fix to not incorrectly report invalid theme as existing
jaxwilko Jul 21, 2024
4fd9164
Update modules/system/console/asset/AssetCreate.php
LukeTowers Jul 22, 2024
acc59b9
Added loop when adding multiple bundles and allowed for chaining
jaxwilko Jul 22, 2024
1ba7847
Merge branch 'wip/asset-version-manager' of github.com:wintercms/wint…
jaxwilko Jul 22, 2024
d2e3b64
Made callbacks execute imidiately and removed the load function
jaxwilko Jul 22, 2024
9ea302a
Added fix for tests
jaxwilko Jul 22, 2024
af8bd48
Removed unused property
jaxwilko Jul 22, 2024
072b44a
Renamed dir to make consistant
jaxwilko Jul 22, 2024
9d81d73
Added fix for inconsistant use of quotes
jaxwilko Jul 22, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions modules/system/ServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
use Config;
use DateInterval;
use Event;
use Illuminate\Foundation\Vite;
use Illuminate\Foundation\Vite as LaravelVite;
use Illuminate\Pagination\Paginator;
use Illuminate\Support\Facades\Schema;
use Markdown;
Expand Down Expand Up @@ -145,7 +145,7 @@ protected function registerSingletons()
});

// Register the Laravel Vite singleton
$this->app->singleton(Vite::class, \System\Classes\Vite::class);
$this->app->singleton(LaravelVite::class, \System\Classes\Asset\Vite::class);
}

/**
Expand Down Expand Up @@ -321,19 +321,19 @@ protected function registerConsole()
$this->registerConsoleCommand('plugin.list', \System\Console\PluginList::class);

$this->registerConsoleCommand('mix.compile', Console\Asset\Mix\MixCompile::class);
$this->registerConsoleCommand('mix.config', Console\Asset\Mix\MixConfig::class);
$this->registerConsoleCommand('mix.config', Console\Asset\Mix\MixCreate::class);
$this->registerConsoleCommand('mix.install', Console\Asset\Mix\MixInstall::class);
$this->registerConsoleCommand('mix.list', Console\Asset\Mix\MixList::class);
$this->registerConsoleCommand('mix.watch', Console\Asset\Mix\MixWatch::class);

$this->registerConsoleCommand('vite.compile', Console\Asset\Vite\ViteCompile::class);
$this->registerConsoleCommand('vite.config', Console\Asset\Vite\ViteConfig::class);
$this->registerConsoleCommand('vite.config', Console\Asset\Vite\ViteCreate::class);
$this->registerConsoleCommand('vite.install', Console\Asset\Vite\ViteInstall::class);
$this->registerConsoleCommand('vite.list', Console\Asset\Vite\ViteList::class);
$this->registerConsoleCommand('vite.watch', Console\Asset\Vite\ViteWatch::class);

$this->registerConsoleCommand('npm.run', Console\NpmRun::class);
$this->registerConsoleCommand('npm.update', Console\NpmUpdate::class);
$this->registerConsoleCommand('npm.run', Console\Asset\NpmRun::class);
$this->registerConsoleCommand('npm.update', Console\Asset\NpmUpdate::class);
}

/*
Expand Down
2 changes: 1 addition & 1 deletion modules/system/classes/MailManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@ public function listRegisteredLayouts()
/**
* Registers a callback function that defines mail templates.
* The callback function should register templates by calling the manager's
* registerMailTemplates() function. Thi instance is passed to the
* registerMailTemplates() function. This instance is passed to the
* callback function as an argument. Usage:
*
* MailManager::registerCallback(function ($manager) {
Expand Down
253 changes: 253 additions & 0 deletions modules/system/classes/asset/BundleManager.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
<?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 <[email protected]>
* @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'
],
],
];

/**
* Cache of registration callbacks.
*/
protected array $callbacks = [];

/**
* 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->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
};
});
});
}

/**
* Loads registered asset bundles from modules and plugins
*/
public function loadRegisteredBundles(): void
{
foreach ($this->callbacks as $callback) {
$callback($this);
}
}

/**
* Returns a list of the registered asset bundles.
*/
public function listRegisteredBundles(): array
{
if (empty($this->registeredBundles)) {
$this->loadRegisteredBundles();
}

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): void
{
$this->callbacks[] = $callback;
}

/**
* Registers asset bundles.
*/
public function registerBundles(array $definitions)
{
$this->registeredBundles = array_replace_recursive($this->registeredBundles, $definitions);
}

/**
* Registers a single asset bundle.
*/
public function registerBundle(string $name, array $definition): void
{
$this->registerBundles([$name => $definition]);
}
LukeTowers marked this conversation as resolved.
Show resolved Hide resolved

/**
* Registers a single bundle setup handler.
*/
public function registerSetupHandler(string $name, Closure $closure): void
{
$this->registeredBundles[$name][static::HANDLER_SETUP] = $closure;
}

/**
* Registers a single bundle scaffold handler.
*/
public function registerScaffoldHandler(string $name, Closure $closure): void
{
$this->registeredBundles[$name][static::HANDLER_SCAFFOLD] = $closure;
}
LukeTowers marked this conversation as resolved.
Show resolved Hide resolved

/**
* 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;
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

namespace System\Classes;
namespace System\Classes\Asset;

use InvalidArgumentException;
use RuntimeException;
Expand Down Expand Up @@ -249,6 +249,14 @@ 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`.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

namespace System\Classes;
namespace System\Classes\Asset;

use Cms\Classes\Theme;
use System\Classes\PluginManager;
Expand All @@ -11,16 +11,16 @@
use Winter\Storm\Support\Str;

/**
* Compilable assets using for Node.js compilation and processing.
* Package manager.
*
* 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 Node.js based compilers for processing.
* 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 <[email protected]>
* @copyright Winter CMS Maintainers
*/
class CompilableAssets
class PackageManager
{
use \Winter\Storm\Support\Traits\Singleton;

Expand Down Expand Up @@ -258,7 +258,7 @@ public function getPackage(string $name, bool $includeIgnored = false): array
* 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 `CompilableAssets` class will look for a `package.json` file for Node dependencies, and a config
* 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
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

namespace System\Classes;
namespace System\Classes\Asset;

use Illuminate\Foundation\Vite as LaravelVite;
use Illuminate\Support\Facades\App;
Expand All @@ -27,12 +27,12 @@ public function __invoke($entrypoints, $package = null)
// Normalise the package name
$package = strtolower($package);

if (!($compilableAssetPackage = CompilableAssets::instance()->getPackages('vite')[$package] ?? null)) {
if (!($compilableAssetPackage = PackageManager::instance()->getPackages('vite')[$package] ?? null)) {
throw new SystemException('Unable to resolve package: ' . $package);
}

$this->useHotFile(base_path($compilableAssetPackage['path'] . '/public/hot'));
return parent::__invoke($entrypoints, $compilableAssetPackage['path'] . '/public/build');
$this->useHotFile(base_path($compilableAssetPackage['path'] . '/assets/dist/hot'));
return parent::__invoke($entrypoints, $compilableAssetPackage['path'] . '/assets/dist');
}

/**
Expand Down
Loading
Loading