-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
Using AssetMapper to manage web assets of bundles #53912
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
|
@javiereguiluz looking at that list, few of them depend on assets. Maybe better would be to use Packagist to get a list of depending packages for |
Expanding beyond GitHub stars, packages that I know do provide some kind of asset integration:
And just expanding on the Sylius comment, there are 3 of its internal bundles that do provide frontend assets, with its plugins (third-party bundles) regularly providing even more assets. Personally, I do think looking at how bundles might integrate with AssetMapper is an interesting thing, as there is clearly a use case for supporting it. |
Let's ping @weaverryan, @smnandre and @kbond because they are experts in AssetMapper (and many other things) so they can guide us about what to do here. Thanks! |
Well it depends what you* want to do i guess :) *or any bundle developer What kind of "importmap command" you'd like to do for instace ? There are already some integrations.. for example the PagerfantaBundle CSS file can be imported either as a standard CSS file, or via the importmap Personal opinion, I think that bundle should not add assets automatically into the main importmap, but there may be something missing today, to allow user to register all assets from a bundle in a simple click/action (like it's done for routes, or config already) (probably declaring an entrypoint?) |
@smnandre there are two different needs here:
About (1), I'm thinking a workflow like this:
What's that About (2), I'm thinking about problems like this: Imagine that you can use AssetMapper to build your bundle assets (it's not possible today, but just imagine you can). You have a file at {{ importmap('foo') }} Symfony will look for We'd need a way to refer to the bundle importmap ... maybe something like: {{ importmap('@MyBundle/foo') }} Or a way to add the bundle importmap (namespaced somehow) into the application importmap:
|
The concept of "bundle importmap" seems weird to me, as there is in the end only one importmap. So that could lead to conflicts. Imho the concept of "entrypoints" work well for that need. I don't know how that covers (or not) all your needs, but you can already add entries into importmap via recipes (that's what ux packages are doing for instance) -- For your examples, very personal opinion and obviously i'm not talking for anyone else here.. but i'm not sure a bundle should bring bootstrap or tailwind (as it's not recommanded to install PHP dependencies) -- For the dev tool, let's say this binary exists, can you precise me what this command would do ?
(it's a geniune question i try to understand your needs and what you would like to do / how we can ease that part of your work ;) ) |
I think that we're not understanding each other. I'll try to use another example: Currently, AssetMapper only works in this specific scenario:
Now, the scenario we are talking about is completely different:
How do you do that?
In both of these cases you end up with the Now, what I want is: build bundle.css and bundle.js with AssetMapper and link to them in my bundle Twig templates. I can install
|
AssetMapper will never build |
That's why i did not fully understand .. there is currently nothing in assetmapper that would help you bundle/pack those files :| |
@stof yes, "build" is not the correct word ... it doesn't build a single file ... but it helps you build the definitive assets with your own assets and the dependencies. In any case, nobody seems to understand what I'm trying to explain here. So maybe we can close this as "won't fix" and don't allow bundles to use AssetMapper? |
Let's try once more .. if there is something AssetMapper can do for you, let's see how to :) Could you ideally illustrate what files "you" (or any imaginary bundle developer of course) want to deal with, where they come, where you want to use theme, what would do the command files, etc etc ? |
Let's wait and see if @weaverryan and @kbond can chime in here. Maybe they understand the scenarios that I explained above. |
I don't know too much about the asset mapper's internals so I'll let Ryan talk more to that. I know there's more to what your asking but is it true that bundle assets are made available to asset mapper? |
Individually yes
|
The doc with full sentences and less code 😅 ![]() |
Hi, lemme chime in.. I think that this question is actually two separate questions: 1. How do I use AssetMapper /
|
I understand that the Could the Asset Mapper hijack that command or disable it and instead copy things into This is an issue at least when it comes to compiling Sass since the files need to be referenced directly and not through the import map. Example: /* assets/app.scss */
@import '../vendor/namespace/my-bundle/src/Resources/scss/style'; I didn't bother putting the If the Asset Mapper could copy bundle files to /* assets/app.scss */
@import 'bundles/namespacemybundle/scss/style'; It also would resemble the JS pathing which works the same in either case: /* assets/app.js */
import '/bundles/namespacemybundle/js/index.js'; |
Did you try with the configuration explained here : https://symfony.com/doc/current/frontend/asset_mapper.html#importing-assets-outside-of-the-assets-directory ? |
I did not, but I don't think that is a sustainable option since every bundle that has assets will have to include the same instructions of editing # config/packages/asset_mapper.yaml
framework:
asset_mapper:
paths:
- assets/
- vendor/some/package/assets
- vendor/some-other/package/assets
- vendor/another/package/assets
# etc. What if the Asset Mapper supported the I made a similar request over at the SassBundle. It could be like a hard-coded path within the Asset Mapper that cannot be overridden. I assume developers could then It would also reduce duplication of work specifically when using the SassBundle with Bootstrap. The recommendation is to install Bootstrap via composer, then reference the Sass directly in the vendor folder. However, in order to leverage the Bootstrap JS, I have to do an Edit: I guess I'd still have to |
And as usual i failed my copy/paste... this is the section that should help you : https://symfony.com/doc/current/frontend/asset_mapper.html#third-party-bundles-custom-asset-paths
|
Yes, that means I don't have to add the path to |
Did you try ? Because according to the link i posted, it's exactly what's available.
Why would a bundle provide a script not ready to use to an app, when it has no certitude AssetMapper is installed / ImportMap is used ? |
Yes, indeed I have read the docs, and yes, I have tried - not just the way it was described in the docs, but also multiple other ways, because the way it was described in the docs didn't work. The issue is that although asset mapper knows about the
The bundle indeed doesn't care whether you use AssetMapper / ImportMap - it provides a pre-bundled IIFE build which you can reference using |
So: If the bundle has precompiled assets, they are available via asset() and versionned If the bundle has non-precompiled assets, or if you want to import some file (JS or CSS).. you have to require them in the importmap (as it's the only way for importmap to ... know they do exist). On none of those cases you need to use '../../../../vendor/' paths. Is there something you would like me to detail, maybe i'm not clear on something and i'd really like everyone to be happy here :) |
And that is, I believe, kind of the point of this issue. Compare: bin/console importmap:require bootstrap
In contrast: composer require jahudka/awesome-wysiwyg
AssetMapper's ImportMap feature and commands like In other words, we're saying "AssetMapper can't do X, but it's such a common use-case, maybe X could be added to AssetMapper?" and you're responding with "AssetMapper can't do X", without addressing the actual question ("can X be added to AssetMapper?"), which makes me think it's us who aren't explaining ourselves well enough.. |
Oh that's not my intention, and i'm sorry if i made it feel that way ! Before AssetMapper, did you have a solution for this specific scenario (= a bundle exposing assets that you need to import / include, assets that themselves require external dependencies) I understand you don't want to use importmap:require yourself.. would that be more ok with flex recipes ? It is maybe not documented enough but it works (so you can add automatically some import, even entrypoint in the importmap) |
Do you have a bundle example that match your needs, so i could try to test / play with and see what solutions can be found ? |
Hey chiming in on behalf of NelmioApiDocBundle, NelmioApiDocBundle currently comes with some 3rd party assets to help users easily browse their api documentation (swagger-ui & redoc) It would be cool if we could instead utilize AssetMapper to handle importing these 3rd party packages by creating a // importmap.php
return [
'swagger-ui' => [
'version' => '...',
],
'redoc' => [
'version' => '...',
],
]; This could then allow use to use these dependencies with a simple import. import SwaggerUI from 'swagger-ui' I hope that I understood your questions/this discussion correctly and that this might help answer some questions 😅 |
I really think in that case the flex recipe is the best solution, allowing a Bundle to write some lines into the main importmap.php. If you want let's have a talk on Slack / mail to see how i may help you implement this ? |
Sure! You should be able to reach me through Slack in the "Symfony Devs" workspace :) |
If AssetMapper & importmap are the "new standard" of Symfony apps, it stands to reason that bundle developers will want to adopt them, so that their bundles are compatible with the recommended standard practices and tools. If bundle developers only expose pre-built assets, that means that you can run into duplicated bundled dependencies (e.g. two Symfony Forms extensions which both add some feature built on top of TinyMCE would result in TinyMCE being included two times in your project). This is what package & dependency management is for, and as long as There are projects which don't use Flex. I feel that a decent chunk of what AssetMapper would need to do to support this feature is already there. Also, using AM to install local dependencies and Flex to install bundle dependencies (even if it ends up using AM in the background) feels like using two separate tools for the same job. AssetMapper can already resolve dependencies recursively; why should you need Flex to be able to do that same thing for bundles, just because they come from Composer rather than NPM? Flex is a dependency which has many other potentially undesirable side effects in a project. There are already conventions in place for various bundle-related things, like the One of the problems the Flex recipes would need to solve is dependency conflicts. Consider:
If the Flex recipes don't check for this, things will break pretty soon - depending on how the Another scenario: when a new version of Instead: consider if AssetMapper can figure out the location of bundles'
In other words, it would be much closer to an actual package & dependency manager. It would give better DX to bundle developers, who wouldn't have to write complex Flex recipes just to properly install I know that doing this properly is probably a lot of work. But I firmly believe that as long as you actually want to go the "JS/Node/NPM-less" route of working with frontend code (as opposed to just integrating better with existing tooling, such as NPM / Vite / Webpack / ...), then a full-featured package manager built into AssetMapper is a far, far superior solution than Flex recipes. |
There are in this issue many ideas / points / suggestions. And i've read them all. I tried to answer regarding what is already possible, what can be with few modifications, why some can be complex / not possible currently. But now i feel this become a way to broad topic, mixing very different things
I just need to remind a few things here
Even if i'm probably the one to blame, my words not beeing always well chosen (english is not my best talent).. this conversation feels to me a bit tense right now. So I'll now stop answering on this issue, and will post again when i have some news on my side... I'll talk with Djrody to see what we can do, and I'm personaly open to other discussions like this :) Thanks everyone for all your messages / ideas / examples :) |
How does/should AssetMapper work for a Bundle when the Main Project does not use it (yet)? For example, sonata-project/form-extensions ships with some assets, some currently done with webpack. Now, the Main Project does not use the importMap in their code right now. Should/Could the Right now, the webpack stuff causes Problems with absolute/relative paths that might could be solved with AssetMapper (maybe) |
Thank you for this suggestion. |
Friendly ping? Should this still be open? I will close if I don't hear anything. |
@javiereguiluz can you add sonata-project/form-extensions as separate point in your list? i still don't know yet what the best way for Sonata would be |
gut feeling is that there's some way to solve this. How about using the --path option when installing the asset, and point to the relevant vendor directory/file? |
I'd like to give my contribution on this issue as well. I've been facing the same issue while developing a bundle with its own dependency. Starting from the points made by @jahudka about
As said, a bundle may have JS & CSS dependencies that would be required by the customer to be installed via My project's solution was basically as follows:
Then, to the point 2.
The bundle, if it needs to expose his js/css files, it can register its AssetMapper paths & namespace as done for instance in ux-autocomplete too: $container->prependExtensionConfig('framework', [
'asset_mapper' => [
'paths' => [
__DIR__ .'/../../assets/dist' => '@acme/bundle'
]
]
]); Then, it can register any file under that namespace. //src/Resources/importmap.php
'@acme/bundle/bundle.js' => [
'path' => '@acme/bundle/bundle.js' // this maps to $bundleDir/assets/dist/bundle.js
],
'@acme/bundle/bundle.css' => [
'path' => '@acme/bundle/bundle.css', // this maps to $bundleDir/assets/dist/bundle.css
'type' => 'css' // this is important to avoid MIME Type error
] So the App import '@acme/bundle/bundle.js'
i
10000
mport '@acme/bundle/bundle.css' (I believe these last steps are optional because, as mentioned by @smnandre and the docs, bundles files under I have this thing working at the moment. I also have the bundle exposing Stimulus Controllers that I can extend from the app, but I won't dig into that. The advantages of a similar solution are that:
Now my concerns are:
It's not a solution I'd propose in a PR as it is of course, but I hope that would give some input to get this issue's discussion going on and maybe we can come to a good solution for the AM Component . I paste the snippet of code that "merges" the importmap just for reference and to give some input to other people that are facing the same issue. It's not a suggestion of using it in a production environment of course :) private function mergeImportmaps(InputInterface $input, OutputInterface $output, array $originalImportMap, array $bundleImportMaps): array
{
// First, we iterate over all bundles' importmaps and we put all the entries we find
// in a entriesMap, where the Key is the entry name, and the value is another map with bundle as keys, and entry config as value
/** @var array<string, array<string, array>> $entriesMap */
$entriesMap = [];
foreach ($bundleImportMaps as $bundle => $importMap) {
foreach ($importMap as $entryName => $config) {
if (isset($entriesMap[$entryName])) {
[$previousBundle, $previousConfig] = $entriesMap[$entryName];
// In case multiple bundles have the same entry, we keep the one with the higher version. (Here Dedupe would be needed instead)
if (isset($config['version'], $previousConfig['version']) && $config['version'] > $previousConfig['version']) {
$entriesMap[$entryName] = [$bundle, $config];
} elseif (!isset($config['version']) && !isset($previousConfig['version'])) {
// Same version, do nothing.
} else {
// some bundle have version defined, some doesn't. This is a config conflicts we highlight
$output->writeln(sprintf("<warning>Improtmap Conflict for entry <options=bold,underscore>%s</options>: Bundle %s's Version = %s, but Bundle %s's Version = %s</warning>",
$entryName, $bundle, $previousBundle, ($config['version'] ?? 'Not Defined'), $previousBundle['version'] ?? 'Not Defined'));
}
} else {
$entriesMap[$entryName] = [$bundle, $config];
}
}
}
// No we iterate through all the entries we mapped.
// If the entry already exists in the project importmap, we have two cases
// 1. Project's importmap has a higher or same version of all the mapped ones, so we don't merge it
// 2. Project's importmap has a lower version of any of the mapped ones: we ask the user what to do.
// Case 2. can be automated by having a command argument that defines the strategy (--update-versions=true/false)
foreach ($entriesMap as $entryName => $map) {
[$bundle, $config] = $map;
if (!isset($originalImportMap[$entryName])) {
// Simple case, project's import map doesn't have the entry, so we import the latest one.
$originalImportMap[$entryName] = $config;
} else {
if (isset($originalImportMap[$entryName]['version']) && $originalImportMap[$entryName]['version'] > ($config['version'] ?? 0)) {
// Case 1, we keep original import map's entry
// Continue
} elseif (isset($originalImportMap[$entryName]['version'], $config['version']) && $originalImportMap[$entryName]['version'] < $config['version']) {
$updateVersions = $input->getOption('update-versions');
$question = new ConfirmationQuestion(sprintf('Bundle %s requires asset %s with version %s but your project targets version %s. Do you want to update the asset to version %s',
$bundle, $entryName, $config['version'], $originalImportMap[$entryName]['version'], $config['version']));
$questionHelper = $this->getHelper('question');
if ($updateVersions || $questionHelper->ask($input, $output, $question)) {
$originalImportMap[$entryName] = $config;
}
}
}
}
return $originalImportMap;
}
(Then, once the merge is done, the code proceeds to generate the `importmap.php` in the `{kernel.project_dir}/importmap.php`) |
Thanks, @Guarrakesh, nice write-up! |
Thank you for sharing @Guarrakesh ! Some technical comments so you can build your PR on something solid. Using @ for namespaces, without active choice from the user, is a very dangerous thing to do, as any bundle (or dependency of any bundle could pretend to be @symfony or @bootstrap or what not. I’m 99% convinced this is the reason of the « bundles » namespace-y thing :) So at minimum you wiill have to set up a prefix / spécial char / etc. Regarding Stimulus controller I’m not sure to follow, as these are handled by the StimulusBundle, not the AssetMaper You’re right about Bundles beeing automatically registered: that’s the main reason why everything you said possible with your solution is already possible without , BUT the installation without consent of dependencies. Regarding conflicts, you should take a look at the ImportMapManager and the downloader, I think most of your suggested algorithm is already coded and used. But this could not be a problem soon as importmap scopes are now baseline I believe, and this is to me THE most important features of import map files. |
@smnandre thanks for your answer and for the clarification about the bundle prefix. I'll have a look at the
Can you give some more context (or reference links) about importmap scopes? I'm afraid I've never came across them. |
The main idea is to have different versions of a given dependency... resolved differently by ... scop :) when called in /foo/foo.js you can use another/package in version 3.20 Examples and (better) explanations here --> https://github.com/WICG/import-maps?tab=readme-ov-file#scoping-examples |
AssetMapper is great and upgrading web apps to AssetMapper is also great and not too complex.
But, managing bundle's web assets with AssetMapper is not possible at the moment.
To make it work, we'd need, at least, two things:
(1) A way to run
importmap
commands when developing the bundle locally. Maybe include a small binary with the component like we do in other components?(2) A way to reference to bundle's assets in
{{ importmap('...') }}
Twig function. Maybe allow to use the typical bundle logic path such asimportmap('@AcmeBundle/app')
?But before doing anything, we must decide if want to support AssetMapper in bundles.
I looked at the most starred GitHub repos that include the
symfony-bundle
tag and this is what I found:So, not many popular third-party bundles need this feature.
But, there are others that could use it:
Thanks!
The text was updated successfully, but these errors were encountered: