En juillet 2024, le blog officiel Symfony l'a écrit noir sur blanc : migrez depuis Webpack Encore vers AssetMapper. Ce n'est pas une recommandation cosmétique — c'est un changement de paradigme assumé. AssetMapper est stable depuis Symfony 6.4 (novembre 2023) et devient le choix par défaut pour tous les nouveaux projets en Symfony 7.1. Le principe : servir CSS et JavaScript directement via les import maps navigateur, sans Node, sans Webpack, sans étape de build distincte. Ce guide couvre la migration complète d'un projet réel — Webpack Encore + Tailwind CSS + Stimulus — avec mesures d'impact sur le DX et le pipeline CI.
Pourquoi migrer maintenant : le signal officiel de juillet 2024
Le billet symfony.com/blog/upgrading-symfony-websites-to-assetmapper publié en juillet 2024 est important non pas pour ce qu'il annonce, mais pour ce qu'il entérine. Pour la première fois, l'équipe Symfony recommande explicitement de migrer les projets existants. Ce n'est plus « AssetMapper est disponible si tu veux tester », c'est « Webpack Encore n'est plus le chemin recommandé ». La raison de fond : les import maps sont désormais supportées par tous les navigateurs modernes (Chrome, Firefox, Safari 16.4+), ce qui élimine le dernier argument qui justifiait un bundler dans 95 % des projets Symfony.
Le problème concret de Webpack Encore en 2024
- Chaque développeur qui clone le projet doit installer Node.js et lancer
yarn installavant de démarrer l'application — une friction invisible mais réelle - Un changement de logo en production déclenche un pipeline CI complet avec une étape Node séparée de 2 à 4 minutes
- Les conflits de version Node entre projets nécessitent nvm, volta ou des conteneurs dédiés — complexité ajoutée sans valeur métier
- Le
node_modulesde 300 à 500 Mo alourdit les images Docker et ralentit les layers de cache - Les avertissements npm/yarn de dépréciation polluent les logs CI en continu sans aucune valeur d'information
Ce qu'AssetMapper fait — et ce qu'il ne fait pas
AssetMapper n'est pas un bundler, et c'est précisément sa force. Il sert tes fichiers JavaScript et CSS tels quels, en ajoutant une couche de versioning par hash de contenu. Le navigateur résout lui-même les imports bare (import { Controller } from '@hotwired/stimulus') grâce à la balise <script type="importmap"> injectée automatiquement par Twig. En développement, les assets sont servis à la volée par Symfony. En production, php bin/console asset-map:compile copie tout dans public/assets/ avec les noms versionnés.
- Gère nativement : versioning des assets par hash, import maps, CSS natif, JavaScript ESM, Stimulus, UX Components Symfony, intégration Twig avec
importmap()etasset() - Ne gère pas nativement : TypeScript, JSX, Vue SFC — tout ce qui nécessite une transpilation préalable
- Pour TypeScript : le bundle
symfonycasts/typescript-bundleajoute une étape esbuild légère sans revenir à un pipeline Node complet - Compatibilité navigateur : import maps supportées nativement depuis Safari 16.4 (mars 2023), Chrome 89, Firefox 108 — IE11 n'est plus un sujet
Inventaire avant migration : cartographie ton projet Webpack Encore
Avant de toucher à quoi que ce soit, liste ce que ton projet utilise réellement. Dans la majorité des projets Symfony standard, on trouve un entrypoint app.js, quelques contrôleurs Stimulus, Tailwind CSS, et éventuellement une ou deux bibliothèques comme Chart.js ou Flatpickr. C'est exactement le périmètre qu'AssetMapper couvre sans effort.
# Lister les dépendances npm réellement utilisées dans le code
grep -rh "from '" assets/controllers/ | sort -u
# Identifier les entrypoints et styleEntries Webpack Encore
grep -n "addEntry\|addStyleEntry\|enableSassLoader\|enablePostCssLoader" webpack.config.js
# Lister les contrôleurs Stimulus existants
ls -la assets/controllers/
# Vérifier si des UX Components Symfony sont utilisés
cat assets/controllers.json 2>/dev/null || echo "Pas de controllers.json"Installation et configuration d'AssetMapper
La migration se fait en parallèle de Webpack Encore — tu peux garder les deux actifs pendant la transition et ne supprimer Webpack qu'une fois que tout fonctionne avec AssetMapper. La recette Flex crée les fichiers de base (importmap.php, assets/app.js, assets/bootstrap.js) et configure automatiquement le bundle.
# Installer AssetMapper, le bundle Stimulus et le bundle Tailwind
composer require symfony/asset-mapper symfony/asset
composer require symfony/stimulus-bundle
composer require symfonycasts/tailwind-bundle
# Vérifier que la recette Flex a bien créé les fichiers de base
ls importmap.php assets/app.js assets/bootstrap.js# config/packages/asset_mapper.yaml
framework:
asset_mapper:
paths:
- assets/<?php
// importmap.php (généré par Flex, à compléter selon tes besoins)
return [
'app' => [
'path' => './assets/app.js',
'entrypoint' => true,
],
'@hotwired/stimulus' => [
'version' => '3.2.2',
],
'@symfony/stimulus-bundle' => [
'path' => './vendor/symfony/stimulus-bundle/assets/dist/loader.js',
],
'@hotwired/turbo' => [
'version' => '7.3.0',
],
];{# templates/base.html.twig — remplacer les blocs webpack_encore_entry_* #}
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{% block title %}Mon projet{% endblock %}</title>
{{- importmap('app') -}}
</head>
<body>
{% block body %}{% endblock %}
</body>
</html>La fonction Twig importmap('app') fait trois choses : elle injecte la balise <script type="importmap"> avec la table de correspondance des modules, ajoute les <link rel="modulepreload"> pour le préchargement, et charge le point d'entrée app.js. Plus de encore_entry_script_tags() ni de encore_entry_link_tags().
Migrer Stimulus : aucun contrôleur à réécrire
C'est la bonne nouvelle de la migration : les contrôleurs Stimulus ne changent pas d'une ligne. Seul le mécanisme d'enregistrement évolue, et la recette Flex s'en charge automatiquement.
// assets/bootstrap.js — point d'entrée Stimulus avec AssetMapper
import { startStimulusApp } from '@symfony/stimulus-bundle';
const app = startStimulusApp();
// Les contrôleurs dans assets/controllers/*_controller.js sont auto-détectés
// Enregistrement manuel si nécessaire :
// import MyController from './controllers/my_controller.js';
// app.register('my', MyController);
export { app };Besoin d'un expert Symfony ?
Réserver un appel →// assets/app.js — entrypoint principal
import './bootstrap.js';
import './styles/app.css';// assets/controllers/modal_controller.js — inchangé par rapport à Webpack Encore
import { Controller } from '@hotwired/stimulus';
export default class extends Controller {
static targets = ['panel'];
static values = { open: Boolean };
connect() {
this.openValue = false;
}
toggle() {
this.openValue = !this.openValue;
this.panelTarget.classList.toggle('hidden', !this.openValue);
}
close() {
this.openValue = false;
this.panelTarget.classList.add('hidden');
}
}Le fichier assets/controllers.json que généraient les UX Components avec Webpack Encore est remplacé par une convention de nommage : tout fichier assets/controllers/*_controller.js est automatiquement découvert et enregistré. Si tu utilises symfony/ux-chartjs, symfony/ux-dropzone ou d'autres UX Components, ils sont déjà compatibles AssetMapper — leurs assets sont déclarés dans leur propre importmap.php via Flex.
Tailwind CSS sans Node : la solution CLI standalone
C'est le point de friction le plus fréquent dans les migrations. Tailwind CSS a longtemps été associé à un pipeline PostCSS qui nécessitait Node. Depuis Tailwind v3, le binaire CLI standalone (un exécutable unique, sans dépendance Node) permet de compiler le CSS directement. Le bundle symfonycasts/tailwind-bundle encapsule ce binaire et l'intègre proprement dans le workflow Symfony.
# Le bundle est déjà installé, initialiser la configuration Tailwind
php bin/console tailwind:init
# Le binaire CLI standalone (~15 Mo) est téléchargé automatiquement
# dans var/tailwind/ lors du premier build
php bin/console tailwind:build# config/packages/tailwind.yaml
symfonycasts_tailwind:
input_css: assets/styles/app.css
# Le CSS compilé est écrit dans var/tailwind/tailwind.built.css
# puis servi via AssetMapper comme n'importe quel autre asset/* assets/styles/app.css — identique à ton fichier Webpack Encore */
@tailwind base;
@tailwind components;
@tailwind utilities;
/* Tes classes custom avec @apply fonctionnent exactement pareil */
.btn-primary {
@apply bg-blue-600 text-white px-4 py-2 rounded-lg font-medium
hover:bg-blue-700 transition-colors duration-200;
}
.card {
@apply bg-white rounded-xl shadow-sm border border-gray-100 p-6;
}# Développement : watch avec rebuild automatique sur changement de template
php bin/console tailwind:build --watch
# Production : minification incluse
php bin/console tailwind:build --minify
php bin/console asset-map:compileLe binaire Tailwind est mis en cache dans var/tailwind/. En CI, tu peux le committer pour éviter le téléchargement à chaque pipeline. Alternativement, configure un cache CI sur ce répertoire — le binaire est déterministe, le cache est toujours valide.
Bibliothèques tierces : importmap:require à la place de npm install
Pour toute bibliothèque disponible sur npm et distribuée en format ESM — Chart.js, Flatpickr, Alpine.js, Sortable.js — la commande importmap:require remplace yarn add. Elle interroge le CDN jspm.io, résout les dépendances transitives, et met à jour importmap.php automatiquement. L'option --download rapatrie les fichiers localement dans assets/vendor/, ce qui est recommandé en production pour éliminer toute dépendance à un CDN externe au runtime.
# Ajouter Chart.js (résout automatiquement les dépendances)
php bin/console importmap:require chart.js
# Version précise
php bin/console importmap:require flatpickr@4.6.13
# Télécharger localement (recommandé en production)
# Évite toute dépendance au CDN au runtime
php bin/console importmap:require chart.js --download
php bin/console importmap:require flatpickr@4.6.13 --download
# Mettre à jour toutes les dépendances
php bin/console importmap:updateImpact sur le pipeline CI/CD : avant/après
La migration vers AssetMapper supprime l'étape Node.js du pipeline CI. Sur un projet standard, passer de setup-node + yarn install + yarn encore production à tailwind:build --minify + asset-map:compile économise 2 à 4 minutes par pipeline. Sur 50 déploiements par mois, c'est une heure de CI récupérée, une image Docker allégée de 300 à 500 Mo, et un Dockerfile enfin lisible sans stage multi-phase dédié au build JS.
# Avant — GitHub Actions avec Webpack Encore (~3 min)
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'yarn'
- name: Install JS dependencies
run: yarn install --frozen-lockfile
- name: Build assets
run: yarn encore production
# Après — AssetMapper (~25 s)
- name: Build Tailwind CSS
run: php bin/console tailwind:build --minify
- name: Compile assets
run: php bin/console asset-map:compileChecklist avant de supprimer Webpack Encore
- Tester chaque page dans les navigateurs cibles — les import maps sont supportées depuis Safari 16.4 (mars 2023), Chrome 89 et Firefox 108 ; prévoir un polyfill (
es-module-shims) uniquement si ton audience cible des versions antérieures - Vérifier que toutes les bibliothèques tierces utilisées sont disponibles en format ESM sur jspm.io — les bibliothèques uniquement distribuées en CommonJS nécessitent une alternative ou un shim
- Committer
importmap.phpdans git — c'est l'équivalent fonctionnel dupackage.json, il doit absolument être versionné - Ajouter
var/tailwind/tailwind-standaloneau.gitignoreet configurer un cache CI dédié survar/tailwind/pour éviter le téléchargement du binaire à chaque pipeline - Supprimer
webpack.config.js,package.jsonetyarn.lockuniquement après avoir validé l'ensemble du projet en environnement de staging — garder les deux systèmes en parallèle le temps de la validation
La migration vers AssetMapper n'est pas un sacrifice fonctionnel — c'est une simplification radicale de la chaîne front-end pour les projets Symfony. Supprimer Node.js du flux de développement élimine une catégorie entière de problèmes d'environnement sans contrepartie visible pour les utilisateurs finaux. L'équipe Symfony a clairement choisi une direction avec la version 7.1 : aligner le développement JavaScript sur les standards navigateur natifs plutôt que maintenir une abstraction de build supplémentaire. Pour la grande majorité des projets Symfony — portails B2B, backoffices, sites institutionnels, SaaS monolithiques — AssetMapper est désormais la réponse par défaut.
Besoin d'un expert Symfony ?
20 ans d'expérience sur l'écosystème PHP/Symfony.