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

Integrate Vite & Rework Mix #1141

Merged
merged 63 commits into from
Jul 16, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
eb3573f
Initial wip
jaxwilko May 31, 2024
989f1cf
Switched implementation to use PackageJson
jaxwilko Jun 5, 2024
5743fbb
Added fix for packages being resolved as an object in json
jaxwilko Jun 5, 2024
d6a15ce
Added vite function to twig
jaxwilko Jun 5, 2024
c847c1a
Added vite commands, renamed old commands and added abstraction
jaxwilko Jun 5, 2024
c373d9a
Added vite config stubs
jaxwilko Jun 5, 2024
ea92c49
Added laravel plugin to install list
jaxwilko Jun 5, 2024
0eaa431
Added fix for eslint parsing fixtures
jaxwilko Jun 5, 2024
5d53b4d
Added fix for undefined keys
jaxwilko Jun 5, 2024
96f1bc6
Revert workflow change
jaxwilko Jun 5, 2024
bf96f7f
Added comment and return type
jaxwilko Jun 5, 2024
1e4ff21
Added comments and small tweaks
jaxwilko Jun 5, 2024
08cca0c
Apply suggestions from code review
jaxwilko Jun 5, 2024
e35a1a0
Added common aliases
jaxwilko Jun 6, 2024
914de7d
Merge branch 'wip/vite-mix-v2' of github.com:wintercms/winter into wi…
jaxwilko Jun 6, 2024
eacd3c2
Remove duplicated type information
LukeTowers Jun 6, 2024
eb95c3c
Apply suggestions from code review
LukeTowers Jun 6, 2024
1500557
Added reordered params and script handling for PackageJson
jaxwilko Jun 7, 2024
6b2b30e
Added fix for getPackage returning incorrect package config
jaxwilko Jun 7, 2024
4a2151e
Added fix for checking package registered
jaxwilko Jun 7, 2024
5aa3248
Added fixes for mix:compile with incorrect config paths
jaxwilko Jun 7, 2024
97576c6
Added Vite unit test
jaxwilko Jun 7, 2024
3a98a96
Added eslint ignores
jaxwilko Jun 7, 2024
8a696dc
Added fix to enable passing package name to vite twig function
jaxwilko Jun 9, 2024
ec7680b
Added default to .mjs file to fix module conflict
jaxwilko Jun 11, 2024
0eb0a99
Updated fixtures and added css stubs
jaxwilko Jun 11, 2024
3f5bbca
Added support for setting up default stub files
jaxwilko Jun 11, 2024
bf954fc
Added postcss config when setting up tailwind config
jaxwilko Jun 11, 2024
c3f8099
Apply suggestions from code review
jaxwilko Jun 11, 2024
d55b517
Added helper for generating vite tags
jaxwilko Jun 11, 2024
0568b54
Merge
jaxwilko Jun 11, 2024
d46a830
Added config tests
jaxwilko Jul 3, 2024
5f50119
Added fix for namespace
jaxwilko Jul 3, 2024
d82978a
Added refactor to use artisan helper
jaxwilko Jul 3, 2024
11ca249
Merge branch 'develop' into wip/vite-mix-v2
jaxwilko Jul 3, 2024
647f285
Added fix for unset position
jaxwilko Jul 5, 2024
744245b
Added support for --disable-tty
jaxwilko Jul 5, 2024
8a18c9b
Added support for --disable-tty to vite commands
jaxwilko Jul 5, 2024
2c3f3a5
Added tests
jaxwilko Jul 5, 2024
e5e2cc1
Cleaned up test with artisan command
jaxwilko Jul 5, 2024
8bd7043
Added confirmation for overriding configured package
jaxwilko Jul 10, 2024
c708a01
Added blocks to vite config refresh paths
jaxwilko Jul 10, 2024
d02926c
Added extra tests and fixes for PackageJson
jaxwilko Jul 11, 2024
ba8cadc
Added metapackage list and added js stubs
jaxwilko Jul 11, 2024
78de514
Added js/css inputs as default for all configs
jaxwilko Jul 11, 2024
3c5d45a
Added compile on exit for vite watch
jaxwilko Jul 11, 2024
c55bb79
Added export-ignore for package.json
jaxwilko Jul 11, 2024
e2a8da0
Added fix for mixed case package names coming from PluginManager
jaxwilko Jul 11, 2024
25d3c43
Added addVite() method to AssetMaker
jaxwilko Jul 11, 2024
49962d6
Added fix so mix & vite stub paths match
jaxwilko Jul 11, 2024
f5626be
Added fix for incorrect signature in npm:run
jaxwilko Jul 12, 2024
41eaffb
Added vite asset render to twig scripts tag
jaxwilko Jul 12, 2024
fd27f1b
Update modules/system/classes/CompilableAssets.php
LukeTowers Jul 14, 2024
be4e50f
Update modules/system/classes/PackageJson.php
LukeTowers Jul 14, 2024
f55d171
Update modules/system/console/asset/AssetConfig.php
LukeTowers Jul 14, 2024
c3fadc0
Added registration method name generation
jaxwilko Jul 14, 2024
6aa24cc
Merge branch 'wip/vite-mix-v2' of github.com:wintercms/winter into wi…
jaxwilko Jul 14, 2024
1d9257e
Update modules/cms/twig/Extension.php
jaxwilko Jul 14, 2024
2905f62
Swapped exception
jaxwilko Jul 14, 2024
6409346
Added fix for incorrect error message
jaxwilko Jul 14, 2024
287dae5
Swapped exception
jaxwilko Jul 14, 2024
72f1e29
Removed auto populate of config on install without config
jaxwilko Jul 14, 2024
9b252d6
Merge branch 'develop' into wip/vite-mix-v2
jaxwilko Jul 14, 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
9 changes: 9 additions & 0 deletions modules/cms/twig/Extension.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use Block;
use Event;
use Illuminate\Foundation\Vite;
use Twig\Extension\AbstractExtension as TwigExtension;
use Twig\TwigFilter as TwigSimpleFilter;
use Twig\TwigFunction as TwigSimpleFunction;
Expand Down Expand Up @@ -51,6 +52,7 @@ public function getFunctions(): array
new TwigSimpleFunction('content', [$this, 'contentFunction'], $options),
new TwigSimpleFunction('component', [$this, 'componentFunction'], $options),
new TwigSimpleFunction('placeholder', [$this, 'placeholderFunction'], ['is_safe' => ['html']]),
new TwigSimpleFunction('vite', [$this, 'viteFunction'], $options),
];
}

Expand Down Expand Up @@ -166,6 +168,13 @@ public function themeFilter($url): string
return $this->controller->themeUrl($url);
}

public function viteFunction(array $arguments, string $base)
{
$vite = app(Vite::class);
$vite->useHotFile(base_path($base . '/public/hot'));
return $vite($arguments, $base . '/public/build');
}

/**
* Opens a layout block.
*/
Expand Down
23 changes: 17 additions & 6 deletions modules/system/ServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Config;
use DateInterval;
use Event;
use Illuminate\Foundation\Vite;
use Illuminate\Pagination\Paginator;
use Illuminate\Support\Facades\Schema;
use Markdown;
Expand Down Expand Up @@ -142,6 +143,8 @@ protected function registerSingletons()
$this->app->singleton('backend.auth', function () {
return \Backend\Classes\AuthManager::instance();
});

$this->app->singleton(Vite::class, Vite::class);
jaxwilko marked this conversation as resolved.
Show resolved Hide resolved
}

/**
Expand Down Expand Up @@ -316,12 +319,20 @@ protected function registerConsole()
$this->registerConsoleCommand('plugin.rollback', \System\Console\PluginRollback::class);
$this->registerConsoleCommand('plugin.list', \System\Console\PluginList::class);

$this->registerConsoleCommand('mix.install', \System\Console\MixInstall::class);
$this->registerConsoleCommand('mix.update', \System\Console\MixUpdate::class);
$this->registerConsoleCommand('mix.list', \System\Console\MixList::class);
$this->registerConsoleCommand('mix.compile', \System\Console\MixCompile::class);
$this->registerConsoleCommand('mix.watch', \System\Console\MixWatch::class);
$this->registerConsoleCommand('mix.run', \System\Console\MixRun::class);
$this->registerConsoleCommand('mix.compile', Console\Asset\Mix\MixCompile::class);
$this->registerConsoleCommand('mix.config', Console\Asset\Mix\MixConfig::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.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);
}

/*
Expand Down
307 changes: 307 additions & 0 deletions modules/system/classes/CompilableAssets.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,307 @@
<?php namespace System\Classes;
LukeTowers marked this conversation as resolved.
Show resolved Hide resolved

use Cms\Classes\Theme;
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;

/**
* Compilable assets using 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.
jaxwilko marked this conversation as resolved.
Show resolved Hide resolved
*
* @package winter\wn-system-module
* @author Ben Thomson <git@alfreido.com>, Jack Wilkinson <me@jackwilky.com>
jaxwilko marked this conversation as resolved.
Show resolved Hide resolved
* @author Winter CMS
jaxwilko marked this conversation as resolved.
Show resolved Hide resolved
*/
class CompilableAssets
{
use \Winter\Storm\Support\Traits\Singleton;

/**
* The filename that stores the package definition.
*/
protected string $packageJson = 'package.json';

/**
* List of package types and registration methods
jaxwilko marked this conversation as resolved.
Show resolved Hide resolved
*/
protected array $compilableConfigs = [
'mix' => [
'registrationMethod' => 'registerMixPackages',
'configFile' => 'winter.mix.js'
],
'vite' => [
'registrationMethod' => 'registerVitePackages',
jaxwilko marked this conversation as resolved.
Show resolved Hide resolved
'configFile' => 'vite.config.js'
]
];

/**
* A list of packages registered for mixing.
LukeTowers marked this conversation as resolved.
Show resolved Hide resolved
*/
protected array $packages = [];

/**
* Registered callbacks.
*/
protected static array $callbacks = [];

/**
* Constructor.
*/
public function init(): void
{
$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()
LukeTowers marked this conversation as resolved.
Show resolved Hide resolved
* {
* return [
* 'package-name-1' => 'winter.mix.js',
* 'package-name-2' => 'assets/js/build.js',
* ];
* }
*
* public function registerVitePackages()
LukeTowers marked this conversation as resolved.
Show resolved Hide resolved
* {
* return [
* 'package-name-1' => 'vite.config.js',
* 'package-name-2' => 'assets/js/build.js',
* ];
* }
*/
foreach ($this->compilableConfigs as $type => $config) {
$packages = PluginManager::instance()->getRegistrationMethodValues($config['registrationMethod']);
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, this allows the
* @param string $name
* @param array $config
* @return void
*/
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(): void
{
// Call callbacks
foreach (static::$callbacks as $callback) {
$callback($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(bool $includeIgnored = false, string $type = 'mix'): array
LukeTowers marked this conversation as resolved.
Show resolved Hide resolved
{
$packages = $this->packages[$type] ?? [];

ksort($packages);

if (!$includeIgnored) {
return array_filter($packages, function ($package) {
return !($package['ignored'] ?? false);
});
}

return $packages;
}

/**
* 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 CompilableAssets class will look for a `package.json` file for Node dependencies, and a config
jaxwilko marked this conversation as resolved.
Show resolved Hide resolved
* 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. 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);
$path = Str::after($pinfo['dirname'], base_path() . DIRECTORY_SEPARATOR);
$configFile = $pinfo['basename'];

// Require $configFile to be a JS file
$extension = File::extension($configFile);
if ($extension !== 'js') {
throw new SystemException(
sprintf('Compilable 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 compilable 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 compilable package; it has already been registered at %s.',
$name,
$this->packages[$name]['config']
));
}

$package = "$path/{$this->packageJson}";
$config = $path . DIRECTORY_SEPARATOR . $configFile;

// Check for any existing package that already registers the given compilable config path
foreach ($this->packages[$type] ?? [] as $packageName => $settings) {
if ($settings['config'] === $config) {
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' => $path,
'package' => $package,
'config' => $config,
'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
$packageJson = new PackageJson(base_path($this->packageJson));
return $packageJson->hasIgnoredPackage($packagePath);
}
}
Loading
Loading