Séparation des frontends dans un projet Laravel

Explorer les étapes pour scinder les packages et configurations frontend dans une application e-commerce de type marketplace.

Ben
Ben

Contexte

Dans cet article, nous allons explorer les étapes pour scinder les packages et configurations frontend dans une application e-commerce de type marketplace. Ce projet est structuré autour de trois interfaces distinctes :

  • Front-Customer : L'interface utilisateur destinée aux clients, développée avec Vue.js et TailwindCSS.
  • Front-Seller : Le panneau de gestion des vendeurs, basé sur la TALL Stack (TailwindCSS, AlpineJS, Laravel, Livewire).
  • Front-Admin : Le back-office des administrateurs, également construit avec la TALL Stack.

L'objectif est de découpler les configurations de ces trois espaces afin de permettre une évolution indépendante des librairies utilisées. Comme il s'agit d'un projet existant, nous cherchons une transition progressive sans refonte complète. De plus, nous souhaitons conserver les assets dans le répertoire resources de Laravel.

Périmètre

Nous allons nous concentrer sur la scission des configurations suivantes :

  • package.json
  • webpack
  • vite
  • postcss
  • tailwindcss

Le but est de permettre aux espaces Admin et Seller de fonctionner indépendamment, tout en maintenant l'intégrité du frontend Customer. Cette étape ne concerne que la configuration technique et ne prévoit pas une refonte complète du code.

Architecture cible

La structure cible du projet sera la suivante :

Copier
project-root/
│-- front-customer/
│   │-- package.json
│   │-- tailwind.customer.config.js
│   │-- vite.customer.config.mjs
│   │-- postcss.customer.config.js
│-- front-seller/
│   │-- package.json
│   │-- tailwind.seller.config.js
│   │-- vite.seller.config.mjs
│   │-- postcss.seller.config.js
│-- front-admin/
│   │-- package.json
│   │-- tailwind.admin.config.js
│   │-- vite.admin.config.mjs
│   │-- postcss.admin.config.js
│-- package.json (racine)

project-root/
│-- front-customer/
│   │-- package.json
│   │-- tailwind.customer.config.js
│   │-- vite.customer.config.mjs
│   │-- postcss.customer.config.js
│-- front-seller/
│   │-- package.json
│   │-- tailwind.seller.config.js
│   │-- vite.seller.config.mjs
│   │-- postcss.seller.config.js
│-- front-admin/
│   │-- package.json
│   │-- tailwind.admin.config.js
│   │-- vite.admin.config.mjs
│   │-- postcss.admin.config.js
│-- package.json (racine)

Chaque commande yarn devra être exécutée dans le répertoire approprié.

Mise en place

1. Création des répertoires

Nous commençons par créer les répertoires :

Copier
mkdir front-customer front-seller front-admin
mkdir front-customer front-seller front-admin

2. Initialisation des projets

Dans chaque répertoire, nous initialisons le projet avec :

Copier
yarn init -y
yarn init -y

Puis, installation des dépendances :

Copier
yarn add vite laravel-vite-plugin tailwindcss@3.x autoprefixer postcss --dev
yarn add vite laravel-vite-plugin tailwindcss@3.x autoprefixer postcss --dev

3. Configuration de Vite

Dans front-seller, créons le fichier vite.seller.config.mjs :

Copier
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import { resolve } from 'path';
import postcssConfig from './postcss.seller.config.js';

export default defineConfig({
    root: '../',
    plugins: [
        laravel({
            input: [
                'resources/assets/css/seller/seller.css',
            ],
            publicDirectory: resolve(__dirname, '../public'),
            hotFile: resolve(__dirname, '../public/hot'),
            refresh: true,
        }),
    ],
    server: {
        host: '0.0.0.0',
        port: 5173,
        cors: true,
    },
    build: {
        outDir: resolve(__dirname, '../public/build'),
    },
    css: {
        postcss: postcssConfig,
    }
});

import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import { resolve } from 'path';
import postcssConfig from './postcss.seller.config.js';

export default defineConfig({
    root: '../',
    plugins: [
        laravel({
            input: [
                'resources/assets/css/seller/seller.css',
            ],
            publicDirectory: resolve(__dirname, '../public'),
            hotFile: resolve(__dirname, '../public/hot'),
            refresh: true,
        }),
    ],
    server: {
        host: '0.0.0.0',
        port: 5173,
        cors: true,
    },
    build: {
        outDir: resolve(__dirname, '../public/build'),
    },
    css: {
        postcss: postcssConfig,
    }
});

Explication des éléments de la configuration Vite

  • root: '../' : Définit le répertoire racine relatif pour Vite. Ici, cela permet de référencer les fichiers tout en conservant l'organisation du projet.
  • input : Définit les fichiers CSS/JS qui seront pris en compte par Vite pour le hot reload et la compilation.
  • publicDirectory : Définit le répertoire de sortie des fichiers compilés, généralement public/build.
  • hotFile : Indique le fichier permettant à Laravel de détecter si le mode de développement est actif.
  • refresh : Active le rechargement automatique des fichiers lorsque des modifications sont détectées.
  • server :
    • host: '0.0.0.0' : Rend le serveur accessible sur le réseau local.
    • port: 5173 : Définit le port par défaut utilisé par Vite en développement. Pour pouvoir lancer les 3 vite en simultané pensez a mettre un port différent a chaque config.
    • cors: true : Active le support des requêtes CORS.
  • build :
    • outDir : Définit le répertoire de sortie des fichiers construits pour la production.
  • css.postcss : Spécifie un fichier de configuration PostCSS personnalisé pour la gestion des styles CSS.

Mais cela n’impact que les chemins du input.

C’est pour ça que pour les autres chemins de fichier j’ai du resolve(__dirname et reconstruire le chemin par rapport à ça.

Enfin dernier point, je n’ai rien su faire pour le chemin du fichier de config postcss. Aussi j’ai préféré l’importer.

4. Configuration de PostCSS

Créons le fichier postcss.seller.config.js :

Copier
import tailwindcss from 'tailwindcss';
import autoprefixer from 'autoprefixer';

export default {
    plugins: [
        tailwindcss({ config: './tailwind.seller.config.js' }),
        autoprefixer(),
    ],
};

import tailwindcss from 'tailwindcss';
import autoprefixer from 'autoprefixer';

export default {
    plugins: [
        tailwindcss({ config: './tailwind.seller.config.js' }),
        autoprefixer(),
    ],
};

Copier
module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  }
}
module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  }
}

Exemple venant de la ddoc tailwindcss. https://v3.tailwindcss.com/docs/installation/using-postcss

Mais ici on importe le fichier dans vite config, aussi deux points important sont à noter

  • Vite ne comprend pas module.export
  • Et dans le cas précis d’un import dans vite.config la property plugin ne doit pas être un objet mais un tableau.

Je ne suis pas du tout connaisseur de JS, donc je ne saurais pas l’expliquer (cela doit être éviddent pour un dev JS), mais en tout cas c’est ce qu’il y a dans la doc de Vite.

5. Configuration de TailwindCSS

Créons tailwind.seller.config.js :

Copier
module.exports = {
    content: [
        './resources/views/seller/**/*.blade.php',
        './resources/views/components/seller/**/*.blade.php',
        './resources/views/components/common/**/*.blade.php',
        './resources/views/vendor/**/*.blade.php',
        './resources/views/livewire/common/**/*.blade.php',
        './resources/views/livewire/seller/**/*.blade.php',
        './resources/assets/css/seller/seller.css',
    ],
    theme: {
        extend: {},
    },
    plugins: [],
};

module.exports = {
    content: [
        './resources/views/seller/**/*.blade.php',
        './resources/views/components/seller/**/*.blade.php',
        './resources/views/components/common/**/*.blade.php',
        './resources/views/vendor/**/*.blade.php',
        './resources/views/livewire/common/**/*.blade.php',
        './resources/views/livewire/seller/**/*.blade.php',
        './resources/assets/css/seller/seller.css',
    ],
    theme: {
        extend: {},
    },
    plugins: [],
};

Dans theme vous pouvez écrire tous vos paramètres de customisation color, padding, font, animation,…

6. Mise à jour du package.json

Ajoutons les scripts dev et build dans package.json de front-seller :

Copier
{
  "name": "front-seller",
  "version": "1.0.0",
  "private": true,
  "scripts": {
    "dev": "vite --config vite.seller.config.mjs",
    "build": "vite build --config vite.seller.config.mjs"
  },
  "devDependencies": {
    "autoprefixer": "^10.4.20",
    "dotenv": "^16.4.7",
    "laravel-vite-plugin": "^1.2.0",
    "postcss": "^8.5.1",
    "tailwindcss": "3.x",
    "vite": "^6.1.0"
  }
}

{
  "name": "front-seller",
  "version": "1.0.0",
  "private": true,
  "scripts": {
    "dev": "vite --config vite.seller.config.mjs",
    "build": "vite build --config vite.seller.config.mjs"
  },
  "devDependencies": {
    "autoprefixer": "^10.4.20",
    "dotenv": "^16.4.7",
    "laravel-vite-plugin": "^1.2.0",
    "postcss": "^8.5.1",
    "tailwindcss": "3.x",
    "vite": "^6.1.0"
  }
}

Désormais yarn dev devra être lancé à partir de chaque répertoire, mais rien n’empêche de faire des alias ou des scripts proxy dans le package.json à la racine du projet.

7. Mise à jour des vues Blade

Nous devons maintenant mettre à jour nos fichiers Blade pour inclure les assets avec la directive @vite.

Exemple pour guest.blade.php qui est un layout pour les pages non connectées (exemple page de login):

Copier
<head>
    <meta charset="UTF-8">
    <title>{{ $page_title ?? "Application Seller" }}</title>
    <meta content='width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no' name='viewport'>
    <meta name="robots" content="noindex">
    @vite([
        'resources/assets/css/seller/seller.css',
    ])
</head>

<head>
    <meta charset="UTF-8">
    <title>{{ $page_title ?? "Application Seller" }}</title>
    <meta content='width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no' name='viewport'>
    <meta name="robots" content="noindex">
    @vite([
        'resources/assets/css/seller/seller.css',
    ])
</head>

Pour les images

Il existe plusieurs façons de gérer ça, que vous pouvez trouver dans la documentation Laravel. Nous avons choisie la suivante.

  • On utilise le helper Vite::
  • On indique dans un fichier JS la liste des répertoires d’assets static à référencer
  • Création d’un fichier static-assets.js dans lequel on va mettre le chemin vers nos assets de manière relatif au fichier JS (resources/assets/js/seller/static-assets.js)
Copier
import.meta.glob([
  '../img/**',
]);
import.meta.glob([
  '../img/**',
]);

Ici on lui dit de référencer toutes les images du répertoire img positionné relativement au fichier JS. Pas l’idéal vu l’état du répertoire, mais cela veut aussi dire qu’on va devoir faire un peu de ménage et avoir un répertoire d’image par front et potentiellement un common.

  • Ensuite on ajoute ce fichier comme input dans la config vite
Copier
laravel({
    input: [
        'resources/assets/css/seller/seller.css',
        'resources/assets/js/seller/seller.js',
        'resources/assets/js/seller/static-assets.js',
    ],
    publicDirectory: resolve(__dirname, '../public'),
    hotFile: resolve(__dirname, '../public/hot'),
    refresh: true,
}),
laravel({
    input: [
        'resources/assets/css/seller/seller.css',
        'resources/assets/js/seller/seller.js',
        'resources/assets/js/seller/static-assets.js',
    ],
    publicDirectory: resolve(__dirname, '../public'),
    hotFile: resolve(__dirname, '../public/hot'),
    refresh: true,
}),
  • Enfin on référence le fichier dans notre layout
Copier
@vite([
    'resources/assets/css/seller/seller.css',
    'resources/assets/js/seller/seller.js',
    'resources/assets/js/seller/static-assets.js',
  ])
@vite([
    'resources/assets/css/seller/seller.css',
    'resources/assets/js/seller/seller.js',
    'resources/assets/js/seller/static-assets.js',
  ])
  • et cela donne le code suivant dans la blade
Copier
<img
  src="{{ Vite::asset('resources/assets/img/full-logo.svg') }}"
  alt="Logo"
  class="my-4"
>
<img
  src="{{ Vite::asset('resources/assets/img/full-logo.svg') }}"
  alt="Logo"
  class="my-4"
>

Conclusion

Cette approche permet d'avoir des configurations propres et indépendantes pour chaque espace frontend, améliorant ainsi la maintenabilité du projet. Il sera plus facile d'effectuer des mises à jour et d'introduire des évolutions sans impacter les autres parties du projet.

Crédits

À découvrir également