Utiliser Laravel Mix dans un thème Drupal

Laravel Mix permet d’utiliser relativement facilement Webpack sans trop entrer dans la configuration qui peut souvent devenir complexe. Voici une façon de l’utiliser dans votre thème Drupal afin de builder vos assets en SASS et ES6, de copier automatiquement les images et polices, de surveiller tous les changements et de synchroniser votre navigateur web pendant le développement.

laravel-mix

Nouveau thème

Définissons un nouveau thème, par exemple nommé d8t (comme Drupal 8 theme) :

d8t.info.yml

name: D8t
type: theme
description: 'Custom Drupal 8 theme'
core: 8.x
base theme: classy
regions:
  header: Header # Drupal core, do not remove
  primary\_menu: Primary menu # Drupal core, do not remove
  secondary\_menu: Secondary menu # Drupal core, do not remove
  highlighted: Highlighted # Drupal core, do not remove
  help: Help # Drupal core, do not remove
  content: Content # Drupal core, do not remove
  sidebar\_first: Sidebar first # Drupal core, do not remove
  sidebar\_second: Sidebar second # Drupal core, do not remove
  footer: Footer # Drupal core, do not remove
  breadcrumb: Breadcrumb # Drupal core, do not remove
libraries:
  - d8t/global

Maintenant définissons notre librairie global que nous venons de déclarer. Dans le monde Laravel, on aurait plutôt mis app à la place de global, mais en Drupal un thème peut définir plusieurs librairies. Cette librairie aura les 3 fichiers CSS requis par Drupal ainsi qu’un fichier JavaScript :

d8t.libraries.yml

global:
  version: VERSION
  css:
    base:
      public/css/global/base.css: {}
    layout:
      public/css/global/layout.css: {}
    component:
      public/css/global/components.css: {}
  js:
    public/js/global/global.js: {}

Jusque là, rien de bien nouveau. Créons la structure des assets images, SASS et JS source :

d8t/resources/images/exemple.jpg
d8t/resources/js/global/global.js
d8t/resources/sass/global/base.scss
d8t/resources/sass/global/components.scss
d8t/resources/sass/global/layout.scss

Laravel Mix

A présent initialisons un package de modules node et ajoutons les dépendances et les scripts de build. Voici le résultat final :

package.json

{
  "name": "d8t",
  "version": "1.0.0",
  "main": "index.js",
  "author": "Guillaume Duveau",
  "license": "MIT",
  "devDependencies": {
    "@fortawesome/fontawesome-free": "^5.12.0",
    "browser-sync": "^2.26.7",
    "browser-sync-webpack-plugin": "^2.0.1",
    "cross-env": "^6.0.3",
    "laravel-mix": "^5.0.0",
    "materialize-css": "^1.0.0",
    "node-sass": "^4.13.0",
    "resolve-url-loader": "^3.1.0",
    "sass-loader": "^8.0.0",
    "vue-template-compiler": "^2.6.10"
  },
  "dependencies": {},
  "scripts": {
    "dev": "npm run development",
    "development": "cross-env NODE\_ENV=development node\_modules/webpack/bin/webpack.js --progress --hide-modules --config=node\_modules/laravel-mix/setup/webpack.config.js",
    "watch": "npm run development -- --watch",
    "hot": "cross-env NODE\_ENV=development node\_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node\_modules/laravel-mix/setup/webpack.config.js",
    "prod": "npm run production",
    "production": "cross-env NODE\_ENV=production node\_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node\_modules/laravel-mix/setup/webpack.config.js"
  }
}

J’ai tout de suite ajouté Font Awesome et Materialize CSS, nous verrons plus tard comment les inclure. Ils sont bien sûr facultatifs. Tout le reste est indispensable. Pour les scripts, nous utiliserons principalement watch pour dévélopper et production pour générer les assets finaux de production.

Pour finir, nous ajoutons ce fichier de configuration :

webpack.mix.js

const mix = require('laravel-mix')

mix
  .webpackConfig({
    module: {
      rules: \[
        {
          test: /\\.s\[ac\]ss$/i,
          use: \[
            {
              loader: 'sass-loader',
              options: {
                sassOptions: {
                  includePaths: \[
                    'node\_modules'
                  \]
                },
              },
            },
          \],
        },
      \],
    }
  })
  .setPublicPath('public')
  .setResourceRoot('../../')
  .js('resources/js/app/app.js', 'public/js/app')
  .sass('resources/sass/app/base.scss', 'public/css/app')
  .sass('resources/sass/app/layout.scss', 'public/css/app')
  .sass('resources/sass/app/components.scss', 'public/css/app')
  .sourceMaps(true, 'source-map')
  .browserSync({
    proxy: 'localhost:8080'
  })

La configuration pourrait être bien plus complexe mais c’est déjà pas mal.

Librairies externes SASS et JavaScript

Voici enfin comment nous pouvons utiliser les librairies node modules. Ajoutons pour commencer Font Awesome dans les components :

components.scss

@import '~@fortawesome/fontawesome-free/css/all.css';

Utilisons maintenant Materialize CSS. On pourrait juste tout importer :

components.scss

@import "~materialize-css/sass/materialize";

Personnellement, pour réduire la taille des assets et pour pouvoir customizer les variables de Materialize CSS, je préfère importer à la main juste ce qu’il me faut :

components.scss

/\*\*
 \* The order of imports must be respected.
 \*
 \* @see https://github.com/Dogfalo/materialize/blob/v1-dev/sass/materialize.scss
 \*/

// We need colors for variables, later.
@import "~materialize-css/sass/components/color-variables";
@import "~materialize-css/sass/components/color-classes";

// We want to override materialize-css variables.
@import "components/variables";

// This is necessary.
@import "~materialize-css/sass/components/normalize";
@import "~materialize-css/sass/components/global";

// Now we import only what we need.
@import "~materialize-css/sass/components/grid";
// We override this one.
@import "components/navbar";
@import "~materialize-css/sass/components/typography";
@import "~materialize-css/sass/components/buttons";
@import "~materialize-css/sass/components/forms/input-fields";
@import "~materialize-css/sass/components/sidenav";
@import "~materialize-css/sass/components/waves";

En dernier, nous allons écrire le JavaScript du thème pour avoir le parallaxe du block titre :

global.js

import { Parallax } from 'materialize-css'

Drupal.behaviors.gd8t = {

  attach: function (context, settings) {
    /\*\*
     \* Parallax.
     \*/
    const parallaxElems = document.querySelectorAll('.parallax')
    const parallaxInstances = M.Parallax.init(parallaxElems)
  }

}

Ca fait du bien, du JS moderne dans un thème Drupal, non ?