function GenerateTheme::execute

Same name and namespace in other branches
  1. 9 core/lib/Drupal/Core/Command/GenerateTheme.php \Drupal\Core\Command\GenerateTheme::execute()
  2. 10 core/lib/Drupal/Core/Command/GenerateTheme.php \Drupal\Core\Command\GenerateTheme::execute()

File

core/lib/Drupal/Core/Command/GenerateTheme.php, line 80

Class

GenerateTheme
Generates a new theme based on latest default markup.

Namespace

Drupal\Core\Command

Code

protected function execute(InputInterface $input, OutputInterface $output) : int {
    $io = new SymfonyStyle($input, $output);
    $filesystem = new Filesystem();
    $tmpDir = $this->getUniqueTmpDirPath();
    $destination_theme = $input->getArgument('machine-name');
    $starterkit_id = $input->getOption('starterkit');
    $theme_label = $input->getOption('name');
    $io->writeln("<info>Generating theme {$theme_label} ({$destination_theme}) from {$starterkit_id} starterkit.</info>");
    $destination = trim($input->getOption('path'), '/') . '/' . $destination_theme;
    if (is_dir($destination)) {
        $io->getErrorStyle()
            ->error("Theme could not be generated because the destination directory {$destination} exists already.");
        return 1;
    }
    $starterkit = $this->getThemeInfo($starterkit_id);
    if ($starterkit === NULL) {
        $io->getErrorStyle()
            ->error("Theme source theme {$starterkit_id} cannot be found.");
        return 1;
    }
    $io->writeln("Trying to parse version for {$starterkit_id} starterkit.", OutputInterface::VERBOSITY_DEBUG);
    try {
        $starterkit_version = self::getStarterKitVersion($starterkit, $io);
    } catch (\Exception $e) {
        $io->getErrorStyle()
            ->error($e->getMessage());
        return 1;
    }
    $io->writeln("Using version {$starterkit_version} for {$starterkit_id} starterkit.", OutputInterface::VERBOSITY_DEBUG);
    $io->writeln("Loading starterkit config from {$starterkit_id}.starterkit.yml.", OutputInterface::VERBOSITY_DEBUG);
    try {
        $starterkit_config = self::loadStarterKitConfig($starterkit, $starterkit_version, $theme_label, $input->getOption('description'));
    } catch (\Exception $e) {
        $io->getErrorStyle()
            ->error($e->getMessage());
        return 1;
    }
    $filesystem->mkdir($tmpDir);
    $io->writeln("Copying starterkit to temporary directory for processing.", OutputInterface::VERBOSITY_DEBUG);
    $mirror_iterator = (new Finder())->in($starterkit->getPath())
        ->files()
        ->notName($starterkit_config['ignore'])
        ->notPath($starterkit_config['ignore']);
    $filesystem->mirror($starterkit->getPath(), $tmpDir, $mirror_iterator);
    $io->writeln("Modifying and renaming files from starterkit.", OutputInterface::VERBOSITY_DEBUG);
    $patterns = [
        'old' => self::namePatterns($starterkit->getName(), $starterkit->info['name']),
        'new' => self::namePatterns($destination_theme, $theme_label),
    ];
    $filesToEdit = self::createFilesFinder($tmpDir)->contains(array_values($patterns['old']))
        ->notPath($starterkit_config['no_edit']);
    foreach ($filesToEdit as $file) {
        $contents = file_get_contents($file->getRealPath());
        $contents = str_replace($patterns['old'], $patterns['new'], $contents);
        file_put_contents($file->getRealPath(), $contents);
    }
    $filesToRename = self::createFilesFinder($tmpDir)->name(array_map(static fn(string $pattern) => "*{$pattern}*", array_values($patterns['old'])))
        ->notPath($starterkit_config['no_rename']);
    foreach ($filesToRename as $file) {
        $filepath_segments = explode('/', $file->getRealPath());
        $filename = array_pop($filepath_segments);
        $filename = str_replace($patterns['old'], $patterns['new'], $filename);
        $filepath_segments[] = $filename;
        $filesystem->rename($file->getRealPath(), implode('/', $filepath_segments));
    }
    $io->writeln("Updating {$destination_theme}.info.yml.", OutputInterface::VERBOSITY_DEBUG);
    $info_file = "{$tmpDir}/{$destination_theme}.info.yml";
    $info = Yaml::decode(file_get_contents($info_file));
    $info = array_filter(array_merge($info, $starterkit_config['info']), static fn(mixed $value) => $value !== NULL);
    // Ensure the generated theme is not hidden.
    unset($info['hidden']);
    file_put_contents($info_file, Yaml::encode($info));
    $loader = new ClassLoader();
    $loader->addPsr4("Drupal\\{$starterkit->getName()}\\", "{$starterkit->getPath()}/src");
    $loader->register();
    $generator_classname = "Drupal\\{$starterkit->getName()}\\StarterKit";
    if (class_exists($generator_classname)) {
        if (is_a($generator_classname, StarterKitInterface::class, TRUE)) {
            $io->writeln("Running post processing.", OutputInterface::VERBOSITY_DEBUG);
            $generator_classname::postProcess($tmpDir, $destination_theme, $theme_label);
        }
        else {
            $io->getErrorStyle()
                ->error("The {$generator_classname} does not implement \\Drupal\\Core\\Theme\\StarterKitInterface and cannot perform post-processing.");
            return 1;
        }
    }
    else {
        $io->writeln("Skipping post processing, {$generator_classname} not defined.", OutputInterface::VERBOSITY_DEBUG);
    }
    // Move altered theme to final destination.
    $io->writeln("Copying {$destination_theme} to {$destination}.", OutputInterface::VERBOSITY_DEBUG);
    $filesystem->mirror($tmpDir, $destination);
    $io->writeln(sprintf('Theme generated successfully to %s', $destination));
    return 0;
}

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.