diff --git a/core/core.services.yml b/core/core.services.yml index 32e39e055eaa01eeaa04ffd761a26c6b6af09305..0b093c466998e379f41fba837ce6cafa2302d5e1 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -1613,6 +1613,7 @@ services: arguments: [ '@asset.js.collection_grouper', '@asset.js.optimizer', '@theme.manager', '@library.dependency_resolver', '@request_stack', '@file_system', '@config.factory', '@file_url_generator', '@datetime.time', '@language_manager'] asset.js.optimizer: class: Drupal\Core\Asset\JsOptimizer + arguments: ['@logger.channel.default'] asset.js.collection_grouper: class: Drupal\Core\Asset\JsCollectionGrouper asset.js.dumper: diff --git a/core/lib/Drupal/Core/Asset/JsOptimizer.php b/core/lib/Drupal/Core/Asset/JsOptimizer.php index 497add6797943105632a42138e22b7dfc26b8939..83d48ee1a2d3f47667f0d0d8333066d24bc332ee 100644 --- a/core/lib/Drupal/Core/Asset/JsOptimizer.php +++ b/core/lib/Drupal/Core/Asset/JsOptimizer.php @@ -3,15 +3,26 @@ namespace Drupal\Core\Asset; use Drupal\Component\Utility\Unicode; +use Drupal\Core\Utility\Error; use Peast\Formatter\Compact as CompactFormatter; use Peast\Peast; use Peast\Renderer; +use Peast\Syntax\Exception as PeastSyntaxException; +use Psr\Log\LoggerInterface; /** * Optimizes a JavaScript asset. */ class JsOptimizer implements AssetOptimizerInterface { + /** + * Constructs a new JsOptimizer object. + * + * @param \Psr\Log\LoggerInterface $logger + * The logger. + */ + public function __construct(protected readonly LoggerInterface $logger) {} + /** * {@inheritdoc} */ @@ -34,10 +45,27 @@ public function optimize(array $js_asset) { $data = Unicode::convertToUtf8($data, $js_asset['attributes']['charset']); } // Remove comments, whitespace, and optional braces. - $ast = Peast::latest($data)->parse(); - $renderer = new Renderer(); - $renderer->setFormatter(new CompactFormatter()); - return $renderer->render($ast); + try { + $ast = Peast::latest($data)->parse(); + $renderer = new Renderer(); + $renderer->setFormatter(new CompactFormatter()); + return $renderer->render($ast); + } + catch (\Exception $exception) { + if ($exception instanceof PeastSyntaxException) { + $position = $exception->getPosition(); + Error::logException($this->logger, $exception, 'Syntax error: @message, File: @asset_file, Line: @asset_line, Column: @asset_column, Index: @asset_index', [ + '@asset_file' => $js_asset['data'], + '@asset_line' => $position->getLine(), + '@asset_column' => $position->getColumn(), + '@asset_index' => $position->getIndex(), + ]); + } + else { + Error::logException($this->logger, $exception); + } + return $data; + } } /** diff --git a/core/tests/Drupal/Tests/Core/Asset/JsOptimizerUnitTest.php b/core/tests/Drupal/Tests/Core/Asset/JsOptimizerUnitTest.php index 15f21a22cf0062f0e16a860257e069ab8c6b0e10..3c4623ab228fdb1c3df9573a041dce6b8a046296 100644 --- a/core/tests/Drupal/Tests/Core/Asset/JsOptimizerUnitTest.php +++ b/core/tests/Drupal/Tests/Core/Asset/JsOptimizerUnitTest.php @@ -24,8 +24,8 @@ class JsOptimizerUnitTest extends UnitTestCase { */ protected function setUp(): void { parent::setUp(); - - $this->optimizer = new JsOptimizer(); + $logger = $this->createMock('\Psr\Log\LoggerInterface'); + $this->optimizer = new JsOptimizer($logger); } /** @@ -119,6 +119,16 @@ public function providerTestOptimize() { ], file_get_contents($path . 'to_be_minified.js.optimized.js'), ], + 4 => [ + [ + 'type' => 'file', + 'preprocess' => TRUE, + 'data' => $path . 'syntax_error.js', + ], + // When there is a syntax error, the 'optimized' contents are the + // contents of the original file. + file_get_contents($path . 'syntax_error.js'), + ], ]; } diff --git a/core/tests/Drupal/Tests/Core/Asset/js_test_files/syntax_error.js b/core/tests/Drupal/Tests/Core/Asset/js_test_files/syntax_error.js new file mode 100644 index 0000000000000000000000000000000000000000..3794c41c8451c98cfcbbaba6b6839263f3aeebbc --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Asset/js_test_files/syntax_error.js @@ -0,0 +1,13 @@ +/** + * Some comments. + */ + +(function foo() { + // Missing closing parenthesis. + if (true { + 'print 1'; + } + else { + 'print 2'; + } +})