Encore -> AssetMapper Parte 2
Keep on Learning!
If you liked what you've learned so far, dive in! Subscribe to get access to this tutorial plus video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeConseguir que funcionen los archivos CSS de terceros es una de las cosas más complicadas de hacer en AssetMapper. Importarlos así no va a funcionar.
Instalar Bootstrap CSS
Centrémonos primero en Bootstrap. Se trata de un paquete de terceros, e instalamos paquetes de terceros diciendo bin/console importmap:require el nombre del paquete:
php bin/console importmap:require bootstrap
Bootstrap es especialmente interesante porque coge el paquete JavaScript, una dependencia del paquete JavaScript, y también se dio cuenta de que este paquete suele tener un archivo CSS... así que también lo cogió. Las tres cosas se añadieron a importmap.php.
| // ... lines 1 - 15 | |
| return [ | |
| // ... lines 17 - 29 | |
| 'bootstrap' => [ | |
| 'version' => '5.3.2', | |
| ], | |
| '@popperjs/core' => [ | |
| 'version' => '2.11.8', | |
| ], | |
| 'bootstrap/dist/css/bootstrap.min.css' => [ | |
| 'version' => '5.3.2', | |
| 'type' => 'css', | |
| ], | |
| ]; |
No vamos a utilizar el JavaScript de bootstrap en este proyecto. Así que podríamos eliminarlo. Pero lo dejaré porque no hace daño a nada. La verdadera estrella, sin embargo, es este archivo CSS. Copia su ruta. Y en app.css, elimina la línea superior.
Puedes importar CSS de terceros con AssetMapper, pero no puedes hacerlo desde dentro de otro archivo CSS. Bueno, técnicamente puedes, pero la vida es más fácil si lo hacemos desdeapp.js. Di import, y luego pega.
| // ... lines 1 - 8 | |
| import 'bootstrap/dist/css/bootstrap.min.css'; | |
| import './styles/app.css'; | |
| // ... lines 11 - 16 |
Y ahora... ¡Bootstrap cobra vida!
Añadir FontAwesome
Lo siguiente es FontAwesome. Fíjate en que estamos cogiendo un archivo CSS específico del paquete. Una gran diferencia entre Encore y AssetMapper es que si necesitas importar un archivo específico de un paquete, tienes que importmap:require ese archivo, no el paquete en general. Mira bin/console importmap:require y pega:
php bin/console importmap:require @fortawesome/fontawesome-free/css/all.css
Eso coge este único archivo CSS, lo descarga en el proyecto y lo añade aimportmap.php justo aquí. Si tienes curiosidad, estos archivos se descargan en un directorio assets/vendor/.
Entra en app.css, elimina esa línea y añade otra importación para esa ruta.
| // ... lines 1 - 8 | |
| import 'bootstrap/dist/css/bootstrap.min.css'; | |
| import '@fortawesome/fontawesome-free/css/all.css'; | |
| import './styles/app.css'; | |
| // ... lines 12 - 17 |
¡Y ya funciona! Aunque, sobre el tema de FontAwesome, ya no recomiendo usar FontAwesome así. En su lugar, utiliza kits de FontAwesome. O, mejor, renderiza un SVG en línea. Con suerte, pronto tendremos un paquete de iconos de Symfony UX para hacerlo más fácil.
Añadir fuentes CSS
El último elemento en app.css es una fuente. Esto es más complicado. Si ejecutamos importmap:requireseguido sólo del nombre del paquete -sin ruta- siempre descargará el archivo JavaScript principal del paquete. Sólo obtendrá un archivo CSS si importmap:require una ruta a un archivo CSS, como acabamos de hacer.
Vale, ya sé que antes ejecutamos import:require bootstrap y eso sí nos dio un archivo CSS. Permíteme ser más claro. Si ejecutas importmap:require packageName, obtendrás el JavaScript de ese paquete. En algunos casos, como Bootstrap, el paquete anuncia que tiene un archivo CSS. Cuando eso ocurre, AssetMapper lo ve y, efectivamente, ejecuta importmap:require bootstrap/dist/css/bootstrap.min.cssautomáticamente... sólo para ser útil.
De todos modos, sé que necesitamos un archivo CSS. Con Encore, si importabas un paquete desde dentro de un archivo CSS, Encore intentaba encontrar el archivo CSS en el paquete e importarlo. Esto no ocurre con AssetMapper: tenemos que averiguar cuál es la ruta al archivo CSS y, a continuación, solicitarlo.
Me gusta hacer esto en jsDelivr.com. Esta es la CDN que AssetMapper utiliza entre bastidores para obtener paquetes. Busca el paquete. Aparecerá, pero hay uno más abajo de @fontsource-variable. Las fuentes variables pueden ser un poco más eficientes, así que cambiemos a eso. Dentro, ¡eh! Anuncia el archivo CSS principal! Si quisieras un archivo diferente, podrías hacer clic en la pestaña Archivos y navegar hasta encontrar lo que necesitas.
Copia esta ruta hasta el nombre del paquete, luego gira y ejecutaimportmap:require y pega. Pero no necesitamos la versión: sólo el paquete y luego la ruta:
php bin/console importmap:require @fontsource-variable/roboto-condensed/index.min.css
Cópiala y pulsa intro. Descarga el archivo CSS y añade una entrada aimportmap.php.
| // ... lines 1 - 15 | |
| return [ | |
| // ... lines 17 - 43 | |
| '@fontsource-variable/roboto-condensed/index.min.css' => [ | |
| 'version' => '5.0.1', | |
| 'type' => 'css', | |
| ], | |
| ]; |
Por último, elimina la importación de app.css e impórtala de app.js.
| // ... lines 1 - 8 | |
| import 'bootstrap/dist/css/bootstrap.min.css'; | |
| import '@fortawesome/fontawesome-free/css/all.css'; | |
| import '@fontsource-variable/roboto-condensed/index.min.css'; | |
| import './styles/app.css'; | |
| // ... lines 13 - 18 |
Ah, y como hemos cambiado a la fuente variable, en app.css, actualiza la familia de fuentes a Roboto Condensed Variable.
En el sitio, observa la fuente cuando actualice. Ya está Coger esos archivos CSS de terceros puede ser lo más complicado que hagas en AssetMapper.
Ah, y si utilizas Sass o Tailwind, hay bundles de Symfonycasts compatibles con ambos en AssetMapper.
Añadir la extensión .js
Ahora que el estilo está funcionando, vamos a echar un vistazo a nuestro JavaScript. En la consola, tenemos un error: un 404 para algo llamado bootstrap. Eso viene deapp.js: de esta línea de importación. Para solucionarlo, abre app.js y añade .js al final.
| // ... lines 1 - 13 | |
| // start the Stimulus application | |
| import './bootstrap.js'; | |
| // ... lines 16 - 18 |
Con Webpack Encore, estamos ejecutando dentro de un entorno Node. Y Node te permite hacer trampas: si el archivo que estás importando termina en .js, no necesitas incluir el .js. Pero en un entorno JavaScript real, como tu navegador, no puedes hacer eso: el .js es necesario.
Éste es probablemente el mayor cambio que tendrás que hacer al convertir.
stimulus-bridge -> stimulus-bundle
Prueba la página ahora. ¡Siguiente error! Y es importante:
Error al resolver el especificador de módulo
@symfony/stimulus-bridge.
Esto significa que, en algún sitio, estamos importando este paquete... pero el paquete no existe en importmap.php.
Hay dos tipos de importaciones. En primer lugar, si una importación empieza por ./ o ../, es una importación relativa. Ésas son sencillas: estás importando un archivo junto a este archivo. El segundo tipo se llama importación desnuda. Es cuando importas un paquete o un archivo dentro de un paquete. En estos casos, la cadena dentro de la importación debe existir exactamente en importmap.php. Si no es así, verás este error.
La fuente de nuestro error es bootstrap.js. ¿Ves este @symfony/stimulus-bridge? que no existe en importmap.php. La solución, normalmente, es instalarlo.
Pero en este caso, el paquete es específico de Webpack Encore y la solución está relacionada con nuestra migración. Cámbialo por @symfony/stimulus-bundle.
| import { startStimulusApp } from '@symfony/stimulus-bundle'; | |
| // ... lines 2 - 8 |
Y he aquí: ¡esa cadena sí vive dentro de importmap.php! A continuación, la siguiente línea simplifica.
| import { startStimulusApp } from '@symfony/stimulus-bundle'; | |
| // Registers Stimulus controllers from controllers.json and in the controllers/ directory | |
| export const app = startStimulusApp(); | |
| // ... lines 5 - 8 |
Pero hace lo mismo que antes: inicia la app Stimulus y carga nuestros controladores. Si inicias una nueva app Symfony, obtendrás todo esto con la receta. Pero como estamos convirtiendo, necesitamos hacer un poco más de trabajo.
Instalar los paquetes que faltan
Actualiza ahora. Obtenemos exactamente el mismo error pero con un paquete diferente:axios. Ya sabes lo que pasa: en algún sitio, estamos importando esto... pero no vive en importmap.php. En este caso, procede desong-controls_controller.js.
Y esta vez, ¡la solución es instalar este paquete! Gira y ejecuta
php bin/console importmap:require axios
Eso añade axios a importmap.php y ahora... ¡nuestra aplicación está viva! ¡Esto funciona con AssetMapper! Tenemos un frontend moderno y eficaz, todo ello sin sistema de compilación.
Reducción de una dependencia
Ah, pero mira el pie de página: el texto es más oscuro que antes. Antes utilizaba bootstrap 5.1. Pero cuando instalamos bootstrap con AssetMapper, cogió la última 5.3. Y, al parecer, ¡algo cambió!
Podría averiguar qué ha cambiado y solucionarlo... Pero también podemos hacer un downgrade. Actualiza la versión en importmap.php a 5.1.3.
| // ... lines 1 - 15 | |
| return [ | |
| // ... lines 17 - 29 | |
| 'bootstrap' => [ | |
| 'version' => '5.1.3', | |
| ], | |
| // ... lines 33 - 35 | |
| 'bootstrap/dist/css/bootstrap.min.css' => [ | |
| 'version' => '5.1.3', | |
| 'type' => 'css', | |
| ], | |
| // ... lines 40 - 50 | |
| ]; |
Si hiciéramos eso y actualizáramos, nada cambiaría: la versión más reciente se sigue descargando en assets/vendor/. Para sincronizar ese directorio con importmap.php, ejecuta:
php bin/console importmap:install
Piensa que esto es como el composer install del mundo de AssetMapper. Se ha dado cuenta de que hemos cambiado dos paquetes y los ha descargado. Y así de fácil, ¡hemos cruzado la línea de meta! ¡Estamos ejecutando AssetMapper!
A continuación, vamos a dedicar tres minutos a modernizar y simplificar nuestro JavaScript.
11 Comments
Hello!
I am using premium plugins from GSAP. They are not available on CDN and so, can not be installed with importmap:require. How can I continue to use them?
Thx!
Hey @be_tnt
That's a very good question, sadly I don't know the answer, I've never had that problem before. Have you tried opening an issue on the Asset Mapper GitHub repository?
I will. Thx!
If I have the following custom function call on certain pages (not all):
How can I convert this to use AssetMapper, given that
'time-entries-summary-style'is a Webpack entry mapped to an SCSS file like this in the Webpack configuration?What steps are necessary to replace
encore_entry_link_tagswith the AssetMapper equivalent while ensuring it works correctly only on specific pages?Hey @ahmedbhs ,
I think this blog post might be interested for you: https://symfony.com/blog/upgrading-symfony-websites-to-assetmapper - it highlights some migration steps from Encore to AssetMapper.
But in particular, to replace
{{ encore_entry_link_tags('time-entries-summary-style') }}with the AssetMapper I think you should follow these steps:The Webpack Encore entries are so called AssetMapper entrypoints. You need to convert your entries into entrypoints1-to-1. You need to do this manually by editing the importmap.php file. See the blog post I linked above for more examples.
Fix imports in your assets. Now, every time you import files, you must include the file extension. Once again see the example from the blog post.
Replace
encore_entry_link_tags()calls in Twig templates withimportmap()ones.Enable SCSS Compilation in AssetMapper. By default, AssetMapper doesn't process SCSS files. You’ll need to configure it to use the symfonycasts/sass-bundle SCSS processor for this. Though think well if you still need Sass, because native CSS supports a lot of things and most probably you can finally convert your SCSS files into plain CSS files. Then you will simplify your setup a lot.
So. once again, I think that blog post will answer most of your questions, so make sure to read it in full to make the smooth migration. And sorry for the long reply on it!
I hope it helps!
Cheers!
Hi Ryan, I'm following this course to upgrade my app from 6.2 to 7.1. I have been able to follow all the steps discussed in the video without any problem. Unfortunately when I try to import jquery-ui (it's not mentioned in the video, I know) I can't get it to work. I have searched for a solution on the internet without good results. Is it possible that this is too new to find any solution? If not, how could I find a solution to my problem?
the error I'm getting in the console is:
Instead, if I can use jQuery without difficulties in the project, for example, if a
console.log($(".myclass");and of course, everything works correctly in webpackencore, but I want to be up to date with symfony and assetmapper.Any information on this is welcome, thank you very much.
Hey @Juan-D
Are you using Webpack or Asset Mapper?
Cheers!
As I say in my query, I am upgrading from Symfony 6.4 to 7.1 and changing webpack to activemapper. I have gone from 6.2 to 6.4 and from 6.4 to 7.1. I have been able to follow the tutorial without problem except for jquery-ui.
Regards!
Hey Juan,
Your upgrading strategy looks good to me. However, probably would be better to finish the upgrade first, and then, in a new PR, migrate from WebpackEncore to AssetMapper. It would simplify things for upgrading for you, and also it will be more obvious for you if you could easily migrate your project to AssetMapper or it requires more changes in your code.
Ideally, I would recommend you to get rid of jQuery completely in your project. jQuery is not recommended anymore as most of things can be easily done natively in the modern JS now. You can reference this website to see how you can replace jQuery calls with modern native JS: https://youmightnotneedjquery.com/
But if your code is tied very much to jQuery - I understand you, and it would require you some time to refactor. But it's difficult to say what's wrong in your code, because it depends on how (and where) you use jQuery. I don remember for sure, but it might be so that you can't use jQuery globally anymore. But in modern JS you should be able to import it like this:
Or in case of the AssetMapper, you will need to install the package with
bin/console importmap:install jqueryand then in the import specify the exact path to the jquery file to import it correctly, something likeimport $ from './path/to/vendor/path/to/jquery.js'This should be the first step, and you need to make sure jQuery works for you this way. When it works for you, you can try to do the same with
jquery-ui, i.e. install it and import from the specific path where it was installed (I'm not sure if you can import everything at once, probably you would need to be more specific about what to import, e.g.import './path/to/vendor/jquery-ui/ui/widgets/datepicker.js'). It might be so that you may need to import both jquery and jquery-ui, i.e. import jquery first because jquery-ui may need that already. And in theory it should work, but if not - probably because jquery-ui requires the jquery to be globally available, not only for this file scope. And if so, this is a bad practice, but you may try to make the imported jquery global withwindow.$ = window.jQuery = $;. Once again, it's a bad practice, but it might work as a temporary solution before you get rid of jQuery completely.Also, please take a look at the official docs: https://symfony.com/doc/current/frontend/asset_mapper.html#global-variables-like-jquery - this should help as well.
I hope that helps!
Cheers!
Hello Victor,
Of course what you mention is the first thing I did. I have not given many explanations before because I wanted to get to the crux of the question directly. But I have carried out the entire update process in a new branch of my project called update. First I updated the framework completely, then the recipes and then I made the necessary adjustments so that everything worked. Now I'm with the js part, and I haven't had any problems until getting to jQuery-ui. As I mentioned, jqyery is imported without problems but as soon as I do "... importmap:require jquery-ui" when I do the import in my js file, the error mentioned appears, "jQuery is not defined". The first thing I did before anything was to consult the official documentation that you mention, to include "window.$=window.jQuery=$" but this does not work for me. And I have verified that I can use jquery in other ways, although in my case, it is with datepicker, but with autocomplete, although I suppose it does not matter which component to use since it is also part of jquery-ui. I had thought that perhaps importing the component I need explicitly could work, but it is something else that still fails, I imported "jquery-ui/ui/widgets/autocomplete" but this has not worked either.
On the other hand, you are right, I have a fairly strong dependency on jquery since I use other components through jquery, such as owl.carousel, but the import does not work either, but I will try to solve the problem first with jquery-ui and Then I will go to owl.carousel. I think it is related, and if I solve one problem the other will be solved.
Thanks for the help.
Greetings.
Hey @Juan-D
Have you tried to define the
jQueryas a global variable? It should happen before loading any other dependency. My guess is thejquery-uiplugin it's badly written, as many jquery plugins are, they assume that a globaljQueryvariable already existsCheers!
"Houston: no signs of life"
Start the conversation!