Skip to content
This repository was archived by the owner on Mar 5, 2025. It is now read-only.

Issue 1543 twig lint replacement #2062

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
"license": "GPL-2.0",
"require": {
"php": ">=5.6",
"asm89/twig-lint": "^1.0.2",
"composer-plugin-api": "^1.0.0",
"composer/installers": "^1.2.0",
"composer/semver": "^1.4",
Expand All @@ -35,6 +34,7 @@
"phpunit/phpunit": "^4.8",
"squizlabs/php_codesniffer": "^2.7",
"symfony/console": "^3.2",
"symfony/twig-bridge": "^3.3",
"symfony/yaml": "^3.2",
"tivie/php-os-detector": "^1.0",
"wikimedia/composer-merge-plugin": "^1.4.1"
Expand Down
140 changes: 88 additions & 52 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions config/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,10 @@ validate:
twig:
filesets:
- files.twig
# Add any custom Twig filters for linter to ignore.
filters: { }
# Add any custom Twig functions for linter to ignore.
functions: { }
yaml:
filesets:
- files.yaml
Expand Down
13 changes: 13 additions & 0 deletions readme/extending-blt.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,3 +188,16 @@ To modify the behavior of the tests:behat target, you may override BLT's `behat`
#### validate:phpcs

To modify the behavior of the validate:phpcs target, you may copy `phpcs.xml.dist` to `phpcs.xml` in your repository root directory and modify the XML. Please see the [official PHPCS documentation](https://github.com/squizlabs/PHP_CodeSniffer/wiki/Advanced-Usage#using-a-default-configuration-file) for more information.

#### validate:twig

To prevent validation failures on any Twig filters or functions created in custom or contrib module `twig.extension` services, add `filters` and `functions` like so:

validate:
twig:
filters:
- my_filter_1
- my_filter_2
functions:
- my_function_1
- my_function_2
44 changes: 44 additions & 0 deletions src/Robo/Blt.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,12 @@
use Robo\Config\Config;
use Robo\Robo;
use Robo\Runner as RoboRunner;
use Symfony\Bridge\Twig\Command\LintCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Twig\Environment;
use Twig\Loader\FilesystemLoader;

/**
* The BLT Robo application.
Expand Down Expand Up @@ -78,6 +81,7 @@ public function __construct(
$this->configureContainer($container);
$this->addBuiltInCommandsAndHooks();
$this->addPluginsCommandsAndHooks();
$this->addSymfonyCommands($application);
$this->runner = new RoboRunner();
$this->runner->setContainer($container);

Expand Down Expand Up @@ -115,6 +119,46 @@ private function addPluginsCommandsAndHooks() {
$this->commands = array_merge($this->commands, $plugin_commands_hooks);
}

/**
* Adds Symfony (non-Robo) command classes to the application.
*
* @param \Acquia\Blt\Robo\Application $application
*/
protected function addSymfonyCommands(Application $application) {
$twig = new Environment(new FilesystemLoader());

$repo_root = $this->getConfig()->get('repo.root');
$extension_file_contents = file_get_contents($repo_root . '/docroot/core/lib/Drupal/Core/Template/TwigExtension.php');

// Get any custom defined Twig filters to be ignored by linter.
$twig_filters = (array) $this->getConfig()->get('validate.twig.filters');
// Add Twig filters from Drupal TwigExtension to be ignored.
$drupal_filters = [];
if ($matches_count = preg_match_all("#new \\\\Twig_SimpleFilter\('([^']+)',#", $extension_file_contents, $matches)) {
$drupal_filters = $matches[1];
}
$twig_filters = array_merge($twig_filters, $drupal_filters);
foreach ($twig_filters as $filter) {
$twig->addFilter(new \Twig_SimpleFilter($filter, function () {}));
}

// Get any custom defined Twig functions to be ignored by linter.
$twig_functions = (array) $this->getConfig()->get('validate.twig.functions');
// Add Twig functions from Drupal TwigExtension to be ignored.
$drupal_functions = [];
if ($matches_count = preg_match_all("#new \\\\Twig_SimpleFunction\('([^']+)',#", $extension_file_contents, $matches)) {
$drupal_functions = $matches[1];
}
$twig_functions = array_merge($twig_functions, $drupal_functions);
foreach ($twig_functions as $function) {
$twig->addFunction(new \Twig_SimpleFunction($function, function () {}));
}

$command = new LintCommand('validate:twig:files');
$command->setTwigEnvironment($twig);
$application->add($command);
}

/**
* Discovers command classes using CommandFileDiscovery.
*
Expand Down
2 changes: 1 addition & 1 deletion src/Robo/BltTasks.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Acquia\Blt\Robo;

use Acquia\Blt\Robo\Commands\Input\ArrayInput;
use Acquia\Blt\Robo\Common\ArrayManipulator;
use Acquia\Blt\Robo\Common\IO;
use Acquia\Blt\Robo\Config\ConfigAwareTrait;
Expand All @@ -20,7 +21,6 @@
use Robo\Contract\VerbosityThresholdInterface;
use Robo\LoadAllTasks;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Yaml\Yaml;

Expand Down
4 changes: 2 additions & 2 deletions src/Robo/Commands/Git/GitCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,16 @@ public function commitMsgHook($message) {
* @return int
*/
public function preCommitHook($changed_files) {
$changed_files_list = explode("\n", $changed_files);
$this->invokeCommands([
// Passing a file list to be PHPCS will cause all specified files to
// be sniffed, regardless of the extensions or patterns defined in
// phpcs.xml. So, we do not use validate:phpcs:files.
'validate:phpcs' => [],
'validate:twig:files' => ['file_list' => $changed_files],
'validate:twig:files' => ['filename' => $changed_files_list],
'validate:yaml:files' => ['file_list' => $changed_files],
]);

$changed_files_list = explode("\n", $changed_files);
if (in_array(['composer.json', 'composer.lock'], $changed_files_list)) {
$this->invokeCommand('validate:composer', ['file_list' => $changed_files]);
}
Expand Down
33 changes: 33 additions & 0 deletions src/Robo/Commands/Input/ArrayInput.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

namespace Acquia\Blt\Robo\Commands\Input;

use Symfony\Component\Console\Input\ArrayInput as ArrayInputBase;

/**
* ArrayInput class.
*/
class ArrayInput extends ArrayInputBase {

/**
* Escapes a token through escapeshellarg if it contains unsafe chars.
*
* @param array|string $token
*
* @return string
*/
public function escapeToken($token) {
// Account for ArrayInput arguments possibly being arrays to prevent
// warning when casting to string.
// @todo Remove when Drupal allows upgrade to Symfony Console 3.3.9+.
// @see https://github.com/symfony/symfony/issues/24087.
if (is_array($token)) {
foreach ($token as $key => $value) {
$token[$key] = preg_match('{^[\w-]+$}', $value) ? $value : escapeshellarg($value);
}
return implode(' ', $token);
}
return preg_match('{^[\w-]+$}', $token) ? $token : escapeshellarg($token);
}

}
36 changes: 4 additions & 32 deletions src/Robo/Commands/Validate/TwigCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,6 @@ class TwigCommand extends BltTasks {
* Executes Twig validator against all validate.twig.filesets files.
*
* @command validate:twig
*
* @return int
* The exit code of the last executed command in
* $this->executeCommandAgainstFilesets().
*/
public function lintFileSets() {
$this->say("Validating twig syntax for all custom modules and themes...");
Expand All @@ -26,35 +22,11 @@ public function lintFileSets() {
$fileset_ids = $this->getConfigValue('validate.twig.filesets');
$filesets = $fileset_manager->getFilesets($fileset_ids);
$bin = $this->getConfigValue('composer.bin');
$command = "'$bin/twig-lint' lint --only-print-errors '%s'";
$this->executeCommandAgainstFilesets($filesets, $command, TRUE);
}

/**
* Executes Twig validator against a list of files, if in twig.filesets.
*
* @command validate:twig:files
*
* @param string $file_list
* A list of files to scan, separated by \n.
*/
public function lintFileList($file_list) {
$this->say("Linting twig files...");

$files = explode("\n", $file_list);

/** @var \Acquia\Blt\Robo\Filesets\FilesetManager $fileset_manager */
$fileset_manager = $this->getContainer()->get('filesetManager');
$fileset_ids = $this->getConfigValue('validate.twig.filesets');
$filesets = $fileset_manager->getFilesets($fileset_ids);

$bin = $this->getConfigValue('composer.bin');
$command = "'$bin/twig-lint' lint --only-print-errors '%s'";
foreach ($filesets as $fileset_id => $fileset) {
$filesets[$fileset_id] = $fileset_manager->filterFilesByFileset($files, $fileset);
}

$command = "'$bin/blt' 'validate:twig:files' '%s'";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aren't you removing the validate:twig:files command above? Doesn't seem like it makes sense to call it here.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

validate:twig:files gets added in src/Robo/Blt.php line 157.

$this->executeCommandAgainstFilesets($filesets, $command);

// If exception wasn't thrown, checks were successful.
$this->say("All Twig files contain valid syntax.");
}

}