devel_wizard-2.x-dev/src/Plugin/DevelWizard/Spell/LibrarySpell.php
src/Plugin/DevelWizard/Spell/LibrarySpell.php
<?php
declare(strict_types=1);
namespace Drupal\devel_wizard\Plugin\DevelWizard\Spell;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Extension\ModuleExtensionList;
use Drupal\Core\Extension\ProfileExtensionList;
use Drupal\Core\Extension\ThemeExtensionList;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Plugin\PluginFormInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\StringTranslation\TranslationInterface;
use Drupal\Core\Template\TwigEnvironment;
use Drupal\devel_wizard\Attribute\DevelWizardSpell;
use Drupal\devel_wizard\Utils;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Filesystem\Path;
#[DevelWizardSpell(
id: 'devel_wizard_library',
category: new TranslatableMarkup('Code'),
label: new TranslatableMarkup('Library'),
description: new TranslatableMarkup('Generates a library. (set of CSS and JS files)'),
tags: [
'code' => new TranslatableMarkup('Code'),
'library' => new TranslatableMarkup('Library'),
],
)]
class LibrarySpell extends SpellBase implements
PluginFormInterface,
ContainerFactoryPluginInterface {
public static function create(
ContainerInterface $container,
array $configuration,
$plugin_id,
$plugin_definition,
) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('messenger'),
$container->get('logger.channel.devel_wizard_spell'),
$container->get('string_translation'),
$container->get('devel_wizard.utils'),
$container->get('config.factory'),
$container->get('extension.list.module'),
$container->get('extension.list.theme'),
$container->get('extension.list.profile'),
$container->get('twig'),
new Filesystem(),
);
}
/**
* {@inheritdoc}
*/
public function __construct(
array $configuration,
$plugin_id,
$plugin_definition,
MessengerInterface $messenger,
LoggerInterface $logger,
TranslationInterface $stringTranslation,
Utils $utils,
ConfigFactoryInterface $configFactory,
protected ModuleExtensionList $moduleList,
protected ThemeExtensionList $themeList,
protected ProfileExtensionList $profileList,
protected TwigEnvironment $twig,
protected Filesystem $fs,
) {
parent::__construct(
$configuration,
$plugin_id,
$plugin_definition,
$messenger,
$logger,
$stringTranslation,
$utils,
$configFactory,
);
}
/**
* {@inheritdoc}
*/
public function defaultConfiguration(): array {
return [
'extension' => [
'machineName' => '',
],
'library' => [
'idShort' => '',
'files' => [],
],
];
}
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state): array {
$conf = $this->getConfiguration();
$form['extension'] = [
'#type' => 'details',
'#tree' => TRUE,
'#title' => $this->t('Extension'),
'#open' => TRUE,
'machineName' => [
'#type' => 'textfield',
'#required' => TRUE,
'#title' => $this->t('Machine-name'),
'#default_value' => $conf['extension']['machineName'],
'#autocomplete_route_name' => 'devel_wizard.autocomplete.extension_all',
],
];
$form['library'] = [
'#type' => 'details',
'#tree' => TRUE,
'#title' => $this->t('Library'),
'#open' => TRUE,
'idShort' => [
'#type' => 'textfield',
'#required' => TRUE,
'#title' => $this->t('Short ID'),
'#field_prefix' => '<extension machine-name>/',
'#default_value' => $conf['library']['idShort'],
],
'files' => [
'#type' => 'checkboxes',
'#required' => TRUE,
'#title' => $this->t('Files'),
'#default_value' => $conf['library']['files'],
'#options' => [
'css.base' => $this->t('CSS - Base'),
'css.layout' => $this->t('CSS - Layout'),
'css.component' => $this->t('CSS - Component'),
'css.state' => $this->t('CSS - State'),
'css.theme' => $this->t('CSS - Theme'),
'js' => $this->t('JS'),
],
],
];
return $form;
}
/**
* {@inheritdoc}
*/
public function validateConfigurationForm(array &$form, FormStateInterface $form_state): void {
// @todo Extension exists.
}
/**
* {@inheritdoc}
*/
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
$configuration = $form_state->getValue($form['#parents'], []);
$this->setConfiguration($configuration);
}
protected function populateCalculatedConfigurationValues(): static {
parent::populateCalculatedConfigurationValues();
if (empty($this->configuration['extension']['machineName'])) {
throw new \InvalidArgumentException('extension.machineName is required');
}
if (empty($this->configuration['library']['id'])
&& empty($this->configuration['library']['idShort'])
) {
throw new \InvalidArgumentException('library.id or library.idShort is required');
}
if (empty($this->configuration['library']['id'])) {
$this->configuration['library']['id'] = sprintf(
'%s/%s',
$this->configuration['extension']['machineName'],
$this->configuration['library']['idShort'],
);
}
elseif (empty($this->configuration['library']['idShort'])) {
$idParts = explode('/', $this->configuration['library']['id']);
$this->configuration['library']['idShort'] = array_pop($idParts);
}
$this->configuration['library'] += $this->utils->stringVariants(
$this->configuration['library']['id'],
'id',
);
$this->configuration['library'] += $this->utils->stringVariants(
$this->configuration['library']['idShort'],
'idShort',
);
$this->configuration['extension']['type'] = $this->getExtensionType($this->configuration['extension']['machineName']);
$extensionList = match($this->configuration['extension']['type']) {
'module' => $this->moduleList,
'theme' => $this->themeList,
'profile' => $this->profileList,
default => NULL,
};
$this->configuration['extension'] += $this->utils->stringVariants(
$this->configuration['extension']['machineName'],
'machineName',
);
$this->configuration['extension']['dir'] = $extensionList->getPath($this->configuration['extension']['machineName']);
return $this;
}
protected function getExtensionType(string $machineName): ?string {
if ($this->moduleList->exists($machineName)) {
return 'module';
}
if ($this->themeList->exists($machineName)) {
return 'theme';
}
if ($this->profileList->exists($machineName)) {
return 'profile';
}
return NULL;
}
/**
* @throws \Twig\Error\SyntaxError
* @throws \Twig\Error\RuntimeError
* @throws \Twig\Error\LoaderError
*/
protected function doIt(): static {
$this
->doItFiles()
->doItLibrary();
$this->batchContext['sandbox']['finished'] = 1.0;
return $this;
}
/**
* @throws \Twig\Error\SyntaxError
* @throws \Twig\Error\LoaderError
* @throws \Twig\Error\RuntimeError
*/
protected function doItFiles(): static {
$conf = $this->getConfiguration();
foreach (array_keys($conf['library']['files'], TRUE) as $fileType) {
$filePath = Path::join(
$conf['extension']['dir'],
$this->getFilePath($fileType),
);
$fileContent = $this->twig->render(
"@devel_wizard/spell/library/$fileType.twig",
$conf,
);
$this->fs->dumpFile($filePath, $fileContent);
$this->messageFilesystemEntryCreate($filePath);
}
return $this;
}
protected function doItLibrary(): static {
$definition = [];
foreach (array_keys($this->configuration['library']['files'], TRUE) as $fileType) {
$parents = explode('.', $fileType);
$parents[] = $this->getFilePath($fileType);
NestedArray::setValue($definition, $parents, []);
}
if ($this->configuration['library']['files']['js']) {
$definition['dependencies'] = ['core/once'];
}
$filePath = Path::join(
$this->configuration['extension']['dir'],
"{$this->configuration['extension']['machineName']}.libraries.yml",
);
$entries = [
$this->configuration['library']['idShort'] => $definition,
];
$this->utils->ymlFileReplace($filePath, $entries);
$this->messageFilesystemEntryUpdate($filePath);
return $this;
}
protected function getFilePath(string $fileType): string {
$parts = explode('.', $fileType);
$fileExtension = array_shift($parts);
$subType = array_shift($parts);
$filePath = Path::join(
$fileExtension,
"{$this->configuration['extension']['machineName']}.{$this->configuration['library']['idShort']}.$subType.$fileExtension",
);
return str_replace('..', '.', $filePath);
}
}
