From d70e500ce7c63f3f4f1a504dfaae1ac537775f1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Andr=C3=A9?= Date: Sat, 27 Jan 2024 16:10:44 +0100 Subject: [PATCH] [AssetMapper] Fix JavaScript compiler load imports from JS strings --- .../Compiler/JavaScriptImportPathCompiler.php | 9 ++- .../JavaScriptImportPathCompilerTest.php | 58 +++++++++++++++++++ 2 files changed, 65 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/AssetMapper/Compiler/JavaScriptImportPathCompiler.php b/src/Symfony/Component/AssetMapper/Compiler/JavaScriptImportPathCompiler.php index 090b1dc7847c1..be81a58027edf 100644 --- a/src/Symfony/Component/AssetMapper/Compiler/JavaScriptImportPathCompiler.php +++ b/src/Symfony/Component/AssetMapper/Compiler/JavaScriptImportPathCompiler.php @@ -27,8 +27,8 @@ */ final class JavaScriptImportPathCompiler implements AssetCompilerInterface { - // https://regex101.com/r/fquriB/1 - private const IMPORT_PATTERN = '/(?:import\s*(?:(?:\*\s*as\s+\w+|[\w\s{},*]+)\s*from\s*)?|\bimport\()\s*[\'"`](\.\/[^\'"`]+|(\.\.\/)*[^\'"`]+)[\'"`]\s*[;\)]?/m'; + // https://regex101.com/r/qFoeoR/1 + private const IMPORT_PATTERN = '/(?:\'(?:[^\'\\\\]|\\\\.)*\'|"(?:[^"\\\\]|\\\\.)*")|(?:import\s*(?:(?:\*\s*as\s+\w+|[\w\s{},*]+)\s*from\s*)?|\bimport\()\s*[\'"`](\.\/[^\'"`]+|(\.\.\/)*[^\'"`]+)[\'"`]\s*[;\)]?/m'; public function __construct( private readonly ImportMapConfigReader $importMapConfigReader, @@ -42,6 +42,11 @@ public function compile(string $content, MappedAsset $asset, AssetMapperInterfac return preg_replace_callback(self::IMPORT_PATTERN, function ($matches) use ($asset, $assetMapper, $content) { $fullImportString = $matches[0][0]; + // Ignore enquoted strings (e.g. console.log("import 'foo';") + if (!isset($matches[1][0])) { + return $fullImportString; + } + if ($this->isCommentedOut($matches[0][1], $content)) { return $fullImportString; } diff --git a/src/Symfony/Component/AssetMapper/Tests/Compiler/JavaScriptImportPathCompilerTest.php b/src/Symfony/Component/AssetMapper/Tests/Compiler/JavaScriptImportPathCompilerTest.php index c0894825b62aa..e616211b5b9dc 100644 --- a/src/Symfony/Component/AssetMapper/Tests/Compiler/JavaScriptImportPathCompilerTest.php +++ b/src/Symfony/Component/AssetMapper/Tests/Compiler/JavaScriptImportPathCompilerTest.php @@ -67,6 +67,7 @@ public function testCompileFindsCorrectImports(string $input, array $expectedJav ->method('getAssetFromSourcePath') ->willReturnCallback(function ($path) { return match ($path) { + '/project/assets/foo.js' => new MappedAsset('foo.js', '/can/be/anything.js', publicPathWithoutDigest: '/assets/foo.js'), '/project/assets/other.js' => new MappedAsset('other.js', '/can/be/anything.js', publicPathWithoutDigest: '/assets/other.js'), '/project/assets/subdir/foo.js' => new MappedAsset('subdir/foo.js', '/can/be/anything.js', publicPathWithoutDigest: '/assets/subdir/foo.js'), '/project/assets/styles.css' => new MappedAsset('styles.css', '/can/be/anything.js', publicPathWithoutDigest: '/assets/styles.css'), @@ -269,6 +270,63 @@ public static function provideCompileTests(): iterable 'expectedJavaScriptImports' => ['/assets/other.js' => ['lazy' => true, 'asset' => 'other.js', 'add' => true]], ]; + yield 'import_in_double_quoted_string_is_ignored' => [ + 'input' => << [], + ]; + + yield 'import_in_double_quoted_string_with_escaped_quote_is_ignored' => [ + 'input' => << [], + ]; + + yield 'import_in_single_quoted_string_is_ignored' => [ + 'input' => << [], + ]; + + yield 'import_after_a_string_is_parsed' => [ + 'input' => << ['/assets/foo.js' => ['lazy' => true, 'asset' => 'foo.js', 'add' => true]], + ]; + + yield 'import_before_a_string_is_parsed' => [ + 'input' => << ['/assets/other.js' => ['lazy' => true, 'asset' => 'other.js', 'add' => true]], + ]; + + yield 'import_before_and_after_a_string_is_parsed' => [ + 'input' => << [ + '/assets/other.js' => ['lazy' => true, 'asset' => 'other.js', 'add' => true], + '/assets/subdir/foo.js' => ['lazy' => true, 'asset' => 'subdir/foo.js', 'add' => true], + ], + ]; + yield 'bare_import_not_in_importmap' => [ 'input' => 'import "some_module";', 'expectedJavaScriptImports' => [],