Skip to content

Commit e03975d

Browse files
feature #51543 [AssetMapper] Add support for CSS files in the importmap (weaverryan)
This PR was squashed before being merged into the 6.4 branch. Discussion ---------- [AssetMapper] Add support for CSS files in the importmap | Q | A | ------------- | --- | Branch? | 6.4 | Bug fix? | fixes #51417 (fixed this along the way) | New feature? | yes | Deprecations? | no | Tickets | None | License | MIT | Doc PR | TODO - but important! Hi! This is the brainchild of `@nicolas`-grekas & I (mostly him). This PR adds 2 big user-facing features: ## A) CSS Handling including ability to import CSS The `importmap.php` now supports a `'type' => 'css'`: ```php return [ 'app.css' => [ 'path' => 'styles/app.css', 'type' => 'css', ], ``` This, by itself, won't cause the CSS file to be loaded. But it WILL be added to the importmap, though the exact behavior will depend on the entrypoint (see next section). But, in the simplest case, it will output something like this, which adds the `<link rel="stylesheet">` tag to the page if this file is every imported in JS `import 'app.css'`. ```html <script type="importmap">{"imports": { "app.css": "data:application/javascript,const%20d%3Ddocument%2Cl%3Dd.createElement%28%22link%22%29%3Bl.href%3D%22%2Fassets%2Fstyles%2Fapp-f86cc618674db9b39b179e440063aab4.css%22%2Cl.rel%3D%22stylesheet%22%2C%28d.head%7C%7Cd.getElementsByTagName%28%22head%22%29%5B0%5D%29.appendChild%28l%29" }}</script> ``` More commonly, in the same way that AssetMapper finds relative JavaScript imports and adds them to the importmap, it also finds relative CSS imports and adds *those* to the importmap. This allows you to: `import './styles/foo.css'` without needing to add `foo.css` to the importmap. It "just works". This would result in `foo.css` being added to the page via JavaScript unless it is in the import chain of an entrypoint (see next section), in which case it would be a true `<link>` tag. Also, you can now download CSS files via `importmap:require` (or, if a package comes with a CSS file, it will be downloaded and added automatically - i.e. if the package has a `style` key): ```bash # will download `bootstrap` AND `bootstrap/dist/css/bootstrap.min.css` php bin/console importmap:require bootstrap ``` ## B) Auto-preload based on entrypoints Like with Webpack, there is now a concept of "entrypoints". The ONE time you call `{{ importmap() }}` on the page, you will pass the 1 or many "entrypoint" names from `importmap.php` that should be loaded - e.g. `importmap('app')` (the most common) or `importmap(['app', 'checkout'])`. Most simply (and this is true already in AssetMapper), this causes a `<script type="module">import 'app';</script>` to be rendered into the page. But in this PR, it also has another critical role: * For each entrypoint, all of the non-lazy JavaScript dependencies are found. So if `app.js` imports `other.js` imports `yet-another.js` imports `some_styles.css`, using non-lazy imports (i.e. `import './other.js` vs the lazy `import('./other.js')`), then `other.js`, `yet-another.js` and `some_styles.css` are all returned. * For all of these dependencies, they are "preloaded" * `other.js` -> preloaded (i.e. `modulepreload` tag rendered) * `yet-another.js` -> preloaded (i.e. `modulepreload` tag rendered) * `some_styles.css` "preloaded" - i.e. a `<link rel="stylesheet"> is added to the page. The idea is that, if `app.js` is your entrypoint, then *every* non-lazy import in its import chain will need to be downloaded before the JavaScript starts executing. So all files should be preloaded. Additionally, if we find any CSS that is imported in a non-lazy way, we render those as `link` tags. The `preload` option in `importmap.php` is GONE. Preloading is controlled entirely through the entrypoint. This entrypoint logic also affects the ordering of the non-lazy CSS files (i.e. the CSS files that will be rendered as `link` tags). It finds all (in order) non-lazy imported CSS files from the entrypoint and render them as `link` tags in order (like Webpack). I propose the recipe starting `importmap.php` is updated to be: ```php return [ 'app' => [ 'path' => 'app.js', 'entrypoint' => true, ], ]; ``` And then in `assets/app.js`: ```js import './styles/app.css'; ``` ## C) Other Improvements * Fixed #51417 where deploying to a subdirectory didn't work * Added a new event in `asset-map:compile` so other processes can hook into this (will make bundles like TailwindBundle) nicer. * Removed `importmap:export` command: I don't see a need for this * Added basic, conservative checking for commented-out imports - if lines START with `//` or `/*` ``` // both will correctly be ignored // import 'foo'; /* import 'foo'; */ ``` ### TODOs * [ ] Update the 6.4 docs * [ ] update the 6.4 recipe Commits ------- 5060fa139e [AssetMapper] Add support for CSS files in the importmap
2 parents 6e507f7 + 90bb528 commit e03975d

File tree

3 files changed

+19
-11
lines changed

3 files changed

+19
-11
lines changed

DependencyInjection/FrameworkExtension.php

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1339,23 +1339,27 @@ private function registerAssetMapperConfiguration(array $config, ContainerBuilde
13391339
->setArgument(0, $config['missing_import_mode']);
13401340

13411341
$container->getDefinition('asset_mapper.compiler.javascript_import_path_compiler')
1342-
->setArgument(0, $config['missing_import_mode']);
1342+
->setArgument(1, $config['missing_import_mode']);
13431343

13441344
$container
13451345
->getDefinition('asset_mapper.importmap.manager')
1346-
->replaceArgument(2, $config['importmap_path'])
13471346
->replaceArgument(3, $config['vendor_dir'])
13481347
;
13491348

1349+
$container
1350+
->getDefinition('asset_mapper.importmap.config_reader')
1351+
->replaceArgument(0, $config['importmap_path'])
1352+
;
1353+
13501354
$container
13511355
->getDefinition('asset_mapper.importmap.resolver')
13521356
->replaceArgument(0, $config['provider'])
13531357
;
13541358

13551359
$container
13561360
->getDefinition('asset_mapper.importmap.renderer')
1357-
->replaceArgument(2, $config['importmap_polyfill'] ?? ImportMapManager::POLYFILL_URL)
1358-
->replaceArgument(3, $config['importmap_script_attributes'])
1361+
->replaceArgument(3, $config['importmap_polyfill'] ?? ImportMapManager::POLYFILL_URL)
1362+
->replaceArgument(4, $config['importmap_script_attributes'])
13591363
;
13601364

13611365
$container->registerForAutoconfiguration(PackageResolverInterface::class)

Resources/config/asset_mapper.php

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
use Symfony\Component\AssetMapper\AssetMapperRepository;
1919
use Symfony\Component\AssetMapper\Command\AssetMapperCompileCommand;
2020
use Symfony\Component\AssetMapper\Command\DebugAssetMapperCommand;
21-
use Symfony\Component\AssetMapper\Command\ImportMapExportCommand;
2221
use Symfony\Component\AssetMapper\Command\ImportMapInstallCommand;
2322
use Symfony\Component\AssetMapper\Command\ImportMapRemoveCommand;
2423
use Symfony\Component\AssetMapper\Command\ImportMapRequireCommand;
@@ -28,6 +27,7 @@
2827
use Symfony\Component\AssetMapper\Compiler\SourceMappingUrlsCompiler;
2928
use Symfony\Component\AssetMapper\Factory\CachedMappedAssetFactory;
3029
use Symfony\Component\AssetMapper\Factory\MappedAssetFactory;
30+
use Symfony\Component\AssetMapper\ImportMap\ImportMapConfigReader;
3131
use Symfony\Component\AssetMapper\ImportMap\ImportMapManager;
3232
use Symfony\Component\AssetMapper\ImportMap\ImportMapRenderer;
3333
use Symfony\Component\AssetMapper\ImportMap\Resolver\JsDelivrEsmResolver;
@@ -100,6 +100,7 @@
100100
param('kernel.project_dir'),
101101
abstract_arg('public directory name'),
102102
param('kernel.debug'),
103+
service('event_dispatcher')->nullOnInvalid(),
103104
])
104105
->tag('console.command')
105106

@@ -130,17 +131,23 @@
130131

131132
->set('asset_mapper.compiler.javascript_import_path_compiler', JavaScriptImportPathCompiler::class)
132133
->args([
134+
service('asset_mapper.importmap.manager'),
133135
abstract_arg('missing import mode'),
134136
service('logger'),
135137
])
136138
->tag('asset_mapper.compiler')
137139
->tag('monolog.logger', ['channel' => 'asset_mapper'])
138140

141+
->set('asset_mapper.importmap.config_reader', ImportMapConfigReader::class)
142+
->args([
143+
abstract_arg('importmap.php path'),
144+
])
145+
139146
->set('asset_mapper.importmap.manager', ImportMapManager::class)
140147
->args([
141148
service('asset_mapper'),
142149
service('asset_mapper.public_assets_path_resolver'),
143-
abstract_arg('importmap.php path'),
150+
service('asset_mapper.importmap.config_reader'),
144151
abstract_arg('vendor directory'),
145152
service('asset_mapper.importmap.resolver'),
146153
service('http_client'),
@@ -180,6 +187,7 @@
180187
->set('asset_mapper.importmap.renderer', ImportMapRenderer::class)
181188
->args([
182189
service('asset_mapper.importmap.manager'),
190+
service('assets.packages')->nullOnInvalid(),
183191
param('kernel.charset'),
184192
abstract_arg('polyfill URL'),
185193
abstract_arg('script HTML attributes'),
@@ -201,10 +209,6 @@
201209
->args([service('asset_mapper.importmap.manager')])
202210
->tag('console.command')
203211

204-
->set('asset_mapper.importmap.command.export', ImportMapExportCommand::class)
205-
->args([service('asset_mapper.importmap.manager')])
206-
->tag('console.command')
207-
208212
->set('asset_mapper.importmap.command.install', ImportMapInstallCommand::class)
209213
->args([service('asset_mapper.importmap.manager')])
210214
->tag('console.command')

Tests/DependencyInjection/XmlFrameworkExtensionTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ public function testAssetMapper()
8585
$this->assertSame(['zip' => 'application/zip'], $definition->getArgument(2));
8686

8787
$definition = $container->getDefinition('asset_mapper.importmap.renderer');
88-
$this->assertSame(['data-turbo-track' => 'reload'], $definition->getArgument(3));
88+
$this->assertSame(['data-turbo-track' => 'reload'], $definition->getArgument(4));
8989

9090
$definition = $container->getDefinition('asset_mapper.repository');
9191
$this->assertSame(['assets/' => '', 'assets2/' => 'my_namespace'], $definition->getArgument(0));

0 commit comments

Comments
 (0)