Skip to content

Commit 2991b06

Browse files
committed
feature #1240 Adding support for AssetMapper 6.4 (weaverryan)
This PR was squashed before being merged into the 2.x branch. Discussion ---------- Adding support for AssetMapper 6.4 | Q | A | ------------- | --- | Bug fix? | yes | New feature? | no | Issues | None | License | MIT Change for AssetMapper 6.4 support. This is 2 main things: A) Changes needed after symfony/symfony#52349 with removal of trait + absolute import paths B) Outputting `autoimport` via `{{ importmap() }}` function / deprecate `ux_controller_link_tags()`. TODO: * I still need to the React/Svelte/Vue support to double-check it's all good for both 6.3 and 6.4. * Finish lazy autoimport * Make sure `controllers.json` is properly a config cache file resource for appropriate files Cheers! Commits ------- 05bcb98 Adding support for AssetMapper 6.4
2 parents 52dc225 + 05bcb98 commit 2991b06

37 files changed

+558
-125
lines changed

src/React/src/AssetMapper/ReactControllerLoaderAssetCompiler.php

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,11 @@
1111

1212
namespace Symfony\UX\React\AssetMapper;
1313

14+
use Symfony\Component\AssetMapper\AssetDependency;
1415
use Symfony\Component\AssetMapper\AssetMapperInterface;
1516
use Symfony\Component\AssetMapper\Compiler\AssetCompilerInterface;
16-
use Symfony\Component\AssetMapper\Compiler\AssetCompilerPathResolverTrait;
1717
use Symfony\Component\AssetMapper\MappedAsset;
18+
use Symfony\Component\Filesystem\Path;
1819
use Symfony\Component\Finder\Finder;
1920

2021
/**
@@ -24,8 +25,6 @@
2425
*/
2526
class ReactControllerLoaderAssetCompiler implements AssetCompilerInterface
2627
{
27-
use AssetCompilerPathResolverTrait;
28-
2928
public function __construct(
3029
private string $controllerPath,
3130
private array $nameGlobs,
@@ -41,10 +40,15 @@ public function compile(string $content, MappedAsset $asset, AssetMapperInterfac
4140
{
4241
$importLines = [];
4342
$componentParts = [];
44-
$loaderPublicPath = $asset->publicPathWithoutDigest;
4543
foreach ($this->findControllerAssets($assetMapper) as $name => $mappedAsset) {
46-
$controllerPublicPath = $mappedAsset->publicPathWithoutDigest;
47-
$relativeImportPath = $this->createRelativePath($loaderPublicPath, $controllerPublicPath);
44+
// @legacy: backwards compatibility with Symfony 6.3
45+
if (class_exists(AssetDependency::class)) {
46+
$controllerPublicPath = $mappedAsset->publicPathWithoutDigest;
47+
$loaderPublicPath = $asset->publicPathWithoutDigest;
48+
$relativeImportPath = Path::makeRelative($controllerPublicPath, \dirname($loaderPublicPath));
49+
} else {
50+
$relativeImportPath = Path::makeRelative($mappedAsset->sourcePath, \dirname($asset->sourcePath));
51+
}
4852

4953
$controllerNameForVariable = sprintf('component_%s', \count($componentParts));
5054

src/React/src/DependencyInjection/ReactExtension.php

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
namespace Symfony\UX\React\DependencyInjection;
1313

1414
use Symfony\Component\AssetMapper\AssetMapperInterface;
15-
use Symfony\Component\AssetMapper\Compiler\AssetCompilerPathResolverTrait;
1615
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
1716
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
1817
use Symfony\Component\Config\Definition\ConfigurationInterface;
@@ -44,22 +43,19 @@ public function load(array $configs, ContainerBuilder $container)
4443
->setPublic(false)
4544
;
4645

47-
// on older versions, the absence of this trait will trigger an error if the service is loaded
48-
if (trait_exists(AssetCompilerPathResolverTrait::class)) {
49-
$container->setDefinition('react.asset_mapper.react_controller_loader_compiler', new Definition(ReactControllerLoaderAssetCompiler::class))
50-
->setArguments([
51-
$config['controllers_path'],
52-
$config['name_glob'],
53-
])
54-
// run before the core JavaScript compiler
55-
->addTag('asset_mapper.compiler', ['priority' => 100]);
56-
57-
$container->setDefinition('react.asset_mapper.replace_process_env_compiler', new Definition(ReactReplaceProcessEnvAssetCompiler::class))
58-
->setArguments([
59-
'%kernel.debug%',
60-
])
61-
->addTag('asset_mapper.compiler');
62-
}
46+
$container->setDefinition('react.asset_mapper.react_controller_loader_compiler', new Definition(ReactControllerLoaderAssetCompiler::class))
47+
->setArguments([
48+
$config['controllers_path'],
49+
$config['name_glob'],
50+
])
51+
// run before the core JavaScript compiler
52+
->addTag('asset_mapper.compiler', ['priority' => 100]);
53+
54+
$container->setDefinition('react.asset_mapper.replace_process_env_compiler', new Definition(ReactReplaceProcessEnvAssetCompiler::class))
55+
->setArguments([
56+
'%kernel.debug%',
57+
])
58+
->addTag('asset_mapper.compiler');
6359
}
6460

6561
public function prepend(ContainerBuilder $container)

src/React/tests/AssetMapper/ReactControllerLoaderAssetCompilerTest.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,15 @@ public function testCompileDynamicallyAddsContents()
3131
if (str_contains($sourcePath, 'MyReactController')) {
3232
return new MappedAsset(
3333
'MyReactController.js',
34+
'/project/assets/react/controllers/MyReactController.js',
3435
publicPathWithoutDigest: '/assets/react/controllers/MyReactController.js',
3536
);
3637
}
3738

3839
if (str_contains($sourcePath, 'DeeperReactController')) {
3940
return new MappedAsset(
4041
'subdir/DeeperReactController.js',
42+
'/project/assets/react/controllers/subdir/DeeperReactController.js',
4143
publicPathWithoutDigest: '/assets/react/controllers/subdir/DeeperReactController.js',
4244
);
4345
}
@@ -50,7 +52,7 @@ public function testCompileDynamicallyAddsContents()
5052
['*.js']
5153
);
5254

53-
$loaderAsset = new MappedAsset('loader.js', publicPathWithoutDigest: '/assets/symfony/ux-react/loader.js');
55+
$loaderAsset = new MappedAsset('loader.js', '/project/assets/vendor/StimulusBundle/loader.js', publicPathWithoutDigest: '/assets/symfony/ux-react/loader.js');
5456
$startingContents = file_get_contents(__DIR__.'/../../assets/dist/loader.js');
5557

5658
$compiledContents = $compiler->compile($startingContents, $loaderAsset, $assetMapper);

src/StimulusBundle/composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
"symfony/dependency-injection": "^5.4|^6.0|^7.0",
1919
"symfony/finder": "^5.4|^6.0|^7.0",
2020
"symfony/http-kernel": "^5.4|^6.0|^7.0",
21-
"twig/twig": "^2.15.3|^3.4.3"
21+
"twig/twig": "^2.15.3|^3.4.3",
22+
"symfony/deprecation-contracts": "^2.0|^3.0"
2223
},
2324
"require-dev": {
2425
"symfony/asset-mapper": "^6.3|^7.0",

src/StimulusBundle/config/services.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
*/
1313

1414
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
15+
use Symfony\UX\StimulusBundle\AssetMapper\AutoImportLocator;
1516
use Symfony\UX\StimulusBundle\AssetMapper\ControllersMapGenerator;
1617
use Symfony\UX\StimulusBundle\AssetMapper\StimulusLoaderJavaScriptCompiler;
1718
use Symfony\UX\StimulusBundle\Helper\StimulusHelper;
@@ -64,6 +65,15 @@
6465
service('stimulus.asset_mapper.ux_package_reader'),
6566
abstract_arg('controller paths'),
6667
abstract_arg('controllers_json_path'),
68+
// @legacy - only allowing null for framework-bundle 6.3
69+
service('stimulus.asset_mapper.auto_import_locator')->nullOnInvalid(),
70+
])
71+
72+
// @legacy - is removed in 6.3
73+
->set('stimulus.asset_mapper.auto_import_locator', AutoImportLocator::class)
74+
->args([
75+
service('asset_mapper.importmap.config_reader'),
76+
service('asset_mapper'),
6777
])
6878

6979
->set('stimulus.asset_mapper.loader_javascript_compiler', StimulusLoaderJavaScriptCompiler::class)
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\UX\StimulusBundle\AssetMapper;
13+
14+
use Symfony\Component\AssetMapper\AssetMapperInterface;
15+
use Symfony\Component\AssetMapper\ImportMap\ImportMapConfigReader;
16+
use Symfony\Component\AssetMapper\MappedAsset;
17+
use Symfony\UX\StimulusBundle\Ux\UxPackageMetadata;
18+
19+
/**
20+
* Finds the MappedAsset for an "autoimport" string.
21+
*/
22+
class AutoImportLocator
23+
{
24+
public function __construct(
25+
private ImportMapConfigReader $importMapConfigReader,
26+
private AssetMapperInterface $assetMapper,
27+
) {
28+
}
29+
30+
// parts of this method are duplicated & adapted from UxControllersTwigRuntime
31+
public function locateAutoImport(string $path, UxPackageMetadata $packageMetadata): MappedControllerAutoImport
32+
{
33+
// see if this is a mapped asset path
34+
if ($asset = $this->assetMapper->getAsset($path)) {
35+
return new MappedControllerAutoImport($asset->sourcePath, false);
36+
}
37+
38+
$slashPosition = strpos($path, '/');
39+
if (false === $slashPosition) {
40+
throw new \LogicException(sprintf('The autoimport "%s" is not valid.', $path));
41+
}
42+
43+
$parts = explode('/', ltrim($path, '@'));
44+
if (2 > \count($parts)) {
45+
throw new \LogicException(sprintf('The autoimport "%s" is not valid.', $path));
46+
}
47+
$package = implode('/', \array_slice($parts, 0, 2));
48+
$file = implode('/', \array_slice($parts, 2));
49+
50+
if ($package === $packageMetadata->packageName) {
51+
// this is a file local to the ux package
52+
$filePath = $packageMetadata->packageDirectory.'/'.$file;
53+
if (!is_file($filePath)) {
54+
throw new \LogicException(sprintf('An "autoimport" in "controllers.json" refers to "%s". This path could not be found in the asset mapper and the file "%s" does not exist in the package path "%s". And so, the file cannot be loaded.', $path, $filePath, $packageMetadata->packageDirectory));
55+
}
56+
57+
$asset = $this->assetMapper->getAssetFromSourcePath($filePath);
58+
if (!$asset) {
59+
throw new \LogicException(sprintf('An "autoimport" in "controllers.json" refers to "%s". This file was found, but the path is not in the asset mapper. And so, the file cannot be loaded. This is a misconfiguration with the bundle providing this.', $path));
60+
}
61+
62+
return new MappedControllerAutoImport($asset->sourcePath, false);
63+
}
64+
65+
$entry = $this->importMapConfigReader->findRootImportMapEntry($path);
66+
if (!$entry) {
67+
throw new \LogicException(sprintf('The autoimport "%s" could not be found in importmap.php. Try running "importmap:require %s".', $path, $path));
68+
}
69+
70+
return new MappedControllerAutoImport($path, true);
71+
}
72+
}

src/StimulusBundle/src/AssetMapper/ControllersMapGenerator.php

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@
1212
namespace Symfony\UX\StimulusBundle\AssetMapper;
1313

1414
use Symfony\Component\AssetMapper\AssetMapperInterface;
15+
use Symfony\Component\AssetMapper\ImportMap\ImportMapGenerator;
1516
use Symfony\Component\Finder\Finder;
17+
use Symfony\UX\StimulusBundle\Ux\UxPackageMetadata;
1618
use Symfony\UX\StimulusBundle\Ux\UxPackageReader;
1719

1820
/**
@@ -31,6 +33,7 @@ public function __construct(
3133
private UxPackageReader $uxPackageReader,
3234
private array $controllerPaths,
3335
private string $controllersJsonPath,
36+
private ?AutoImportLocator $autoImportLocator = null,
3437
) {
3538
}
3639

@@ -72,7 +75,8 @@ private function loadCustomControllers(): array
7275
$name = str_replace(['_', '/'], ['-', '--'], $name);
7376

7477
$asset = $this->assetMapper->getAssetFromSourcePath($file->getRealPath());
75-
$isLazy = preg_match('/\/\*\s*stimulusFetch:\s*\'lazy\'\s*\*\//i', $asset->content);
78+
$content = $asset->content ?: file_get_contents($asset->sourcePath);
79+
$isLazy = preg_match('/\/\*\s*stimulusFetch:\s*\'lazy\'\s*\*\//i', $content);
7680

7781
$controllersMap[$name] = new MappedControllerAsset($asset, $isLazy);
7882
}
@@ -129,10 +133,37 @@ private function loadUxControllers(): array
129133
throw new \RuntimeException(sprintf('Could not find an asset mapper path that points to the "%s" controller in package "%s", defined in controllers.json.', $controllerName, $packageMetadata->packageName));
130134
}
131135

132-
$controllersMap[$controllerNormalizedName] = new MappedControllerAsset($asset, $lazy);
136+
$autoImports = $this->collectAutoImports($localControllerConfig['autoimport'] ?? [], $packageMetadata);
137+
138+
$controllersMap[$controllerNormalizedName] = new MappedControllerAsset($asset, $lazy, $autoImports);
133139
}
134140
}
135141

136142
return $controllersMap;
137143
}
144+
145+
/**
146+
* @return MappedControllerAutoImport[]
147+
*/
148+
private function collectAutoImports(array $autoImports, UxPackageMetadata $currentPackageMetadata): array
149+
{
150+
// @legacy: Backwards compatibility with Symfony 6.3
151+
if (!class_exists(ImportMapGenerator::class)) {
152+
return [];
153+
}
154+
if (null === $this->autoImportLocator) {
155+
throw new \InvalidArgumentException(sprintf('The "autoImportLocator" argument to "%s" is required when using AssetMapper 6.4', self::class));
156+
}
157+
158+
$autoImportItems = [];
159+
foreach ($autoImports as $path => $enabled) {
160+
if (!$enabled) {
161+
continue;
162+
}
163+
164+
$autoImportItems[] = $this->autoImportLocator->locateAutoImport($path, $currentPackageMetadata);
165+
}
166+
167+
return $autoImportItems;
168+
}
138169
}

src/StimulusBundle/src/AssetMapper/MappedControllerAsset.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ class MappedControllerAsset
2323
public function __construct(
2424
public MappedAsset $asset,
2525
public bool $isLazy,
26+
/**
27+
* @var MappedControllerAutoImport[]
28+
*/
29+
public array $autoImports = [],
2630
) {
2731
}
2832
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\UX\StimulusBundle\AssetMapper;
13+
14+
/**
15+
* @experimental
16+
*
17+
* @author Ryan Weaver <[email protected]>
18+
*/
19+
class MappedControllerAutoImport
20+
{
21+
public function __construct(
22+
public string $path,
23+
public bool $isBareImport
24+
) {
25+
}
26+
}

0 commit comments

Comments
 (0)