Дружим Wordpress и Webpack

Разработку фронтенда сегодня уже тяжело представить без webpack, а все потому что он предоставляет очень удобные возможности для автоматизации многих процессов. Это очень сильно облегчает процесс разработки и само-собой ускоряет его. В этот раз, когда я решил обновить тему для своего блога, я решил попробовать использовать его возможности. Особенно мне хотелось использовать sass при создании темы и добавить postcss с autoprefixer. Ну что же, есть желание, то надо пробовать. 

Поискав в Интернете — ничего толкового не нашел, поэтому решил написать свою конфигурацию. Скажу сразу — она не идеальная и возможно подойдет не всем, но с помощью огромного количества разных плагинов каждый сможет ее переделать под свои нужды. 

В качестве менеджера пакетов я использую yarn. Поэтому заходим в корневую директорию (где у нас находится wordpress) и устанавливаем нужные нам пакеты. В итоге у меня получился вот такой package.json:

{
  "dependencies": {
    "autoprefixer": "^7.1.4",
    "babel": "^6.23.0",
    "babel-loader": "^7.1.2",
    "babel-plugin-transform-builtin-extend": "^1.1.2",
    "babel-polyfill": "^6.26.0",
    "babel-preset-env": "^1.6.0",
    "babili-webpack-plugin": "^0.1.2",
    "copy-webpack-plugin": "^4.0.1",
    "css-loader": "^0.28.7",
    "cssnano": "^3.10.0",
    "extract-text-webpack-plugin": "^3.0.0",
    "file-loader": "^1.1.5",
    "jquery": "^3.2.1",
    "node-sass": "^4.5.3",
    "postcss-loader": "^2.0.6",
    "postcss-smart-import": "^0.7.5",
    "replace-in-file-webpack-plugin": "^1.0.0",
    "sass-loader": "^6.0.6",
    "style-loader": "^0.18.2",
    "webpack": "^3.6.0"
  },
  "browserslist": {
    "production": [
      "iOS >= 7",
      "Chrome >= 38",
      "Firefox >= 30",
      "Explorer >= 11"
    ],
    "development": [
      "iOS >= 7",
      "Chrome >= 38",
      "Firefox >= 30",
      "Explorer >= 11"
    ]
  },
  "scripts": {
    "start": "webpack"
  }
}

Если вы планируете использовать мой набор плагинов, то не забудьте воспользоваться командой yarn install для установки пакетов.

Вот такой набор минимально необходимых плагинов получился. Как вы можете заметить тут я использую postcss и указываю какие браузеры мне необходимо поддерживать. Для него нужен свой фаил конфигурации postcss.config.js, который у меня выглядит вот так:

module.exports = {
    plugins: [
        require('postcss-smart-import')(),
        require('autoprefixer')({remove: false}),
        require('cssnano')({
            preset: 'default',
        }),
    ]
};

Ну и теперь остается добавить только саму конфигурацию вебпака что бы все завелось. Содержимое конфигурационного файла webpack.config.js будет выглядеть следующим образом:

const path = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const ReplaceInFileWebpackPlugin = require('replace-in-file-webpack-plugin');
const webpack = require('webpack');

// Укажите название вашей темы
const THEME_NAME = 'vexell-ru';

// Директория с темами
const appPath = path.join(__dirname, `wp-content/themes/${THEME_NAME}`);
// Основной JS файл в который мы будем подключать все, что нам необходимо
const jsPath = path.join(appPath, '/assets/js/index.js');
// Директория куда будет происходить билд проекта
const outPath = path.join(appPath, '/assets/js/');

// Разделяем Sass файлы и CSS в отдельный фаил
const extractSass = new ExtractTextPlugin({
    filename: '../../style.css'
});

const extractCss = new ExtractTextPlugin({
    filename: '../../style.css'
});

const rules = [
    // Обработка всех JS файлов с помощью Babel. Можем использовать все новые фишки ES
    {
        test: /\.js$/,
        exclude: /(node_modules|bower_components)/,
        use: {
            loader: 'babel-loader',
            options: {
                presets: ['env'],
                "plugins": [
                    ["babel-plugin-transform-builtin-extend", {
                        globals: ["Error"]
                    }]
                ]
            }
        }
    },
    // Подгрузка обычных файлов css. Делал так вначале. Потом все перевел на sass
    {
        test: /\.css$/,
        use: extractCss.extract([
            {
                loader: 'css-loader',
                options: {
                    minimize: true
                }
            },
            'postcss-loader'
        ])
    },
    // Обработка Sass файлов. Минифицирум, делаем постобработку
    {
        rules: [{
            test: /\.scss$/,
            use: extractSass.extract({
                use: [{
                    loader: "css-loader",
                    options: {
                        minimize: true
                    }
                }, {
                    loader: "sass-loader"
                }, {
                    loader: 'postcss-loader'
                }],
                fallback: "style-loader"
            })
        }]
    },
    /* Вместо обычного копирования файлов с новом хэшем в имени 
       добавляем хэш как query атрибут. Использую это в стилях для картинок.
     */
    {
        test: /\.(png|jpg|gif)$/,
        use: [
            {
                loader: 'file-loader',
                options: {
                    context: 'public',
                    name: 'assets/images/[name].[ext]?v=[hash]',
                    publicPath: './',
                },
            },
        ]
    }
];

const plugins = [
    extractCss,
    extractSass,
    /*
      Следующий плагин делает основную магию, которая 
      версионирует каждый раз фаил со стилями и JS.
      Подробнее описано ниже.
    */
    new ReplaceInFileWebpackPlugin([{
        dir: appPath,
        files: ['functions.php'],
        rules: [{
            search: new RegExp('\'_bld_(.*?)\'','ig'),
            replace: function() {
                return `'_bld_${Number(new Date())}'`
            }
        }]
    }]),
    /*
     Для использования jQuery вебпаку нужно указать, что она будет глобальна
     */
    new webpack.ProvidePlugin({
        $: 'jquery',
        jQuery: 'jquery',
        'window.jQuery': 'jquery'
    })
];

module.exports = {
    context: appPath,
    entry: [
        'babel-polyfill',
        jsPath
    ],
    resolve: {
        modules: [path.resolve(__dirname), 'node_modules']
    },
    output: {
        path: outPath,
        publicPath: '/',
        filename: 'build.js'
    },
    module: {
        rules: rules
    },
    plugins: plugins,
    watch: true,
    externals: {
        'jquery': 'jQuery'
    }
};

Как известно, что бы подключить свои стили в wordpress необходимо воспользоваться следующим хуком:

function site_scripts() {
    wp_enqueue_style('font-awesome', get_template_directory_uri() . '/assets/css/font-awesome.css', array(), '4.7.0', 'all' );
   wp_enqueue_style('site-style', get_stylesheet_uri(), array(), '_bld_1507921193074' );
    wp_enqueue_script('site-script', get_template_directory_uri() . '/assets/js/build.js', array('jquery'), '_bld_1507921193074', true );

   wp_localize_script( 'site-script', 'screenReaderText', array(
      'expand'   => esc_html__( 'expand child menu'),
      'collapse' => esc_html__( 'collapse child menu'),
   ) );
}
add_action( 'wp_enqueue_scripts', 'site_scripts' );

Поэтому в нашей конфигурации webpack при каждой новой сборки мы обновляем параметр когда произошла сборка. Replace плагин находит вхождение «_bld_» и заменяет его. Соответсвенно в вашем файле необходимо сначала будет указать версию файлов как «_bld_». Получится примерно так:

wp_enqueue_style('site-style', get_stylesheet_uri(), array(), '_bld_' );

Теперь можно запустить webpack командой yarn start — проект запустится и время сборки будет добавляться автоматически. 

Основной javascript файл у меня содержит все мои необходимые скрипты

import '../css/my.scss';
import 'jquery';
import './libs/navigation';
import './libs/skip-link-focus-fix';
import './libs/custom-scripts';
import './libs/jquery.matchHeight';

Всегда разрабатываемую тему я храню в git — это очень удобно так как есть полная история что происходило (но это совсем другая история). Что бы не хранить весь wordpress в git — я там храню только выбранную тему. Для этого у меня в .gitignore вот такая конфигурация:

.idea/
node_modules/
/*.*
wp-admin/
wp-includes/
wp-content/index.php
wp-content/plugins
wp-content/uploads
wp-content/gallery
wp-content/languages
wp-content/ngg
wp-content/themes/index.php
!.gitignore
!package.json
!webpack.config.js
!postcss.config.js
!wp-config.php
!yarn.lock

Вот собственно и все. Теперь можно облегчить себе жизнь при разработки новой темы и воспользоваться всеми современными возможностями. Есть вопросы ? Задавайте их в комментариях.