5 полезных плагинов для webpack

    Привет, Хабр!

    У webpack'а есть много полезных плагинов, о которых многие не знают и не используют в своих проектах. Под катом я собрал 5 таких, они могут здорово упростить вам жизнь!



    1. Unused files webpack plugin


    В больших фронтенд-проектах всегда много модулей, в которых легко потеряться. Если не проверять проект на неиспользуемые модули, то рано или поздно в нем скопятся мусорные файлы, которые нигде не используются.

    Чтобы знать о неиспользуемых файлах в проекте, добавьте в свой конфиг unused-files-webpack-plugin:

    npm i unused-files-webpack-plugin
    

    /* webpack.config.js */
    
    const { UnusedFilesWebpackPlugin } = require('unused-files-webpack-plugin');
    
    module.exports = {
      /* ... */
      plugins: [
        /* ... */
        new UnusedFilesWebpackPlugin(),
      ],
    };
    

    После установки вы будете получать предупреждения о любых неиспользуемых файлах:

    src/
    ├── index.js
    └── someFile.js (не используется)
    

    WARNING in 
    UnusedFilesWebpackPlugin found some unused files:
    src/someFile.js
    

    Ссылки:

    Github
    NPM

    2. Case sensitive paths webpack plugin


    Во время разработки на OSX часто можно перепутать строчные и заглавные буквы в пути при импорте модуля. На маке сборка соберется, но на других чувствительных к регистру системах она упадет.

    Если хотите избавиться от этой проблемы, нужно поставить case-sensitive-paths-webpack-plugin:

    npm i case-sensitive-paths-webpack-plugin
    

    /* webpack.config.js */
    
    const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
    
    module.exports = {
      /* ... */
      plugins: [
        /* ... */
        new CaseSensitivePathsPlugin(),
      ],
    };
    

    Теперь при импортировании модуля с неправильным регистром вы всегда будете получать ошибку:

    /* index.js */
    
    import someFile from './SOMEFILE';
    

    /* someFile.js */
    
    export default () => {
      console.log('Hello!');
    };
    

    ERROR in index.js
    Module not found: Error: [CaseSensitivePathsPlugin] `SOMEFILE.js` does not match the corresponding path on disk `someFile.js`.
    

    Эту проблему так же можно решить с помощью eslint-плагина import/no-unresolved.

    В typescript есть похожая по работе опция — forceConsistentCasingInFileNames. Но ведет она себя немного иначе: она проверяет, что импортирование модуля из разных мест не отличается по регистру, сама правильность регистра не проверяется.

    Ссылки:

    Github
    NPM

    3. Inspectpack


    В вашем проекте может возникнуть ситуация, что вы и ваши пакеты используют разные версии одной и той же библиотеки. Тогда в бандле появится несколько копий пакета, что легко можно поправить, указав одинаковые версии.

    Inspectpack поможет сразу найти такую ситуацию:

    npm i inspectpack
    

    /* webpack.config.js */
    
    const { DuplicatesPlugin } = require('inspectpack/plugin');
    
    module.exports = {
      /* ... */
      plugins: [
        /* ... */
        new DuplicatesPlugin(),
      ],
    };
    

    Если вы установите плагин, то при появлении копий пакета будет выскакивать предупреждение:

    /* package.json */
    
    {
      /* ... */
      "name": "my-app",
      "dependencies": {
        "lodash": "4.1.0",
        "one": "1.2.3"
      }
    }
    

    /* node_modules/one/package.json */
    
    {
      /* ... */
      "name": "one",
      "dependencies": {
        "lodash": "3.0.0"
      }
    }
    

    /* index.js */
    
    import _ from 'lodash';
    import 'one';
    
    /* ... */
    

    WARNING in Duplicate Sources / Packages - Duplicates found! ️
    
    * Duplicates: Found 2 similar files across 2 code sources (both identical + similar)
      accounting for 703 bundled bytes.
    * Packages: Found 1 packages with 2 resolved, 2 installed, and 2 depended versions.
    
    ## bundle.js
    lodash (Found 2 resolved, 2 installed, 2 depended. Latest 4.1.0.)
      3.0.0 ~/one/~/lodash
        scenario-new-webpack-new-npm-unflattened@* -> one@1.2.3 -> lodash@3.0.0
      4.1.0 ~/lodash
        scenario-new-webpack-new-npm-unflattened@* -> lodash@4.1.0
    

    Пример из реального мира
    Во всех своих проектах для трекинга ошибок я использую sentry. Его javascript SDK разбито на несколько пакетов, у каждого из которых есть зависимость от tslib@^1.9.3.

    В одном из проектов по ошибке в dependencies был объявлен tslib@1.9.0, из-за чего tslib одной версии устанавливался локально для каждого пакета. Пакеты sentry выглядели вот так:


    Бордовым выделены копии tslib

    Parsed size: 121.03 KB
    Gzipped size: 27.16 KB
    

    Благодаря inspectpack проблема нашлась: я удалил из dependencies в package.json tslib@1.9.0, зависимость tslib@^1.9.3 от sentry установилась один раз на верхнем уровне, и пакеты стали весить меньше на 26 КБайт:



    Parsed size: 94.5 KB
    Gzipped size: 26.5 KB
    


    Аналогичный функционал предоставляет duplicate-package-checker-webpack-plugin. Но с ним есть одна проблема — он не показывает несколько вхождений одной версии библиотеки, т.е. проблему в примере под спойдером, где несколько одинаковых версий tslib, он найти не сможет.

    Ссылки:

    Github
    NPM

    4. Circular dependency plugin


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

    Чтобы сразу увидеть круговую зависимость и убрать ее, добавьте circular-dependency-plugin:

    npm i circular-dependency-plugin
    

    /* webpack.config.js */
    
    const CircularDependencyPlugin = require('circular-dependency-plugin');
    
    module.exports = {
      /* ... */
      plugins: [
        /* ... */
        new CircularDependencyPlugin(),
      ],
    };
    

    Теперь при появлении циклической зависимости вы получите предупреждение:

    /* first.js */
    
    import second from './second'
    
    /* ... */
    

    /* second.js */
    
    import first from './first'
    
    /* ... */
    

    WARNING in Circular dependency detected:
    first.js -> second.js -> first.js
    
    WARNING in Circular dependency detected:
    second.js -> first.js -> second.js
    

    Похожую проблему решают правила import/no-cycle для eslint и tslint-no-circular-imports для tslint. У последнего, правда, нет возможности игнорировать импорты типов, интерфейсов и классов, которые используются только для типизации — придется часто писать tslint:disable.

    Ссылки:

    Github
    NPM

    5. Speed measure webpack plugin


    В больших проектах, состоящих из нескольких сотен файлов, сборка может занимать до нескольких минут.

    Замерить каждый шаг сборки и найти проблемы поможет speed-measure-webpack-plugin:

    npm i speed-measure-webpack-plugin
    

    /* webpack.config.js */
    
    const SpeedMeasurePlugin = require("speed-measure-webpack-plugin"); 
    const smp = new SpeedMeasurePlugin();
    
    module.exports = smp.wrap({
      /* ... */
    });
    

    В выводе сборки добавится информация об общем времени сборки, времени выполнения каждого плагина и каждой цепочки лоадеров:

     SMP
    General output time took 48.97 secs
    
     SMP - Plugins
    TerserPlugin took 19.6 secs
    OptimizeCssAssetsWebpackPlugin took 2.65 secs
    MiniCssExtractPlugin took 0.261 secs
    VueLoaderPlugin took 0.216 secs
    
    /* ... */
    
     SMP - Loaders
    mini-css-extract-plugin, and 
    css-loader, and 
    postcss-loader took 21.81 secs
      module count = 33
    
    cache-loader, and 
    babel-loader, and 
    ts-loader, and 
    tslint-loader took 21.63 secs
      module count = 240
    
    /* ... */
    

    Мне он помог найти проблему со скоростью минификации у TerserPlugin: я убрал несколько настроек, которые почти не влияли на размер итоговых файлов, и ускорил его на пару-тройку секунд.

    Ссылки:

    Github
    NPM
    • +30
    • 9,2k
    • 5
    Constanta
    75,64
    Разработка букмекерских продуктов
    Поддержать автора
    Поделиться публикацией

    Похожие публикации

    Комментарии 5

      +1
      Отличная подборка! 4 из 5 взял на вооружение.
        0
        Про дубликаты вопрос.
        Ведь дубликаты в «сторонних» пакетах. Можно конечно залесть в /node_modules/somepkg/package.json и там поправить версию, но выглядит это как грязный хак. Есть способ получше?
          0

          В yarn можно задать в рамках вашего package.json-a
          https://yarnpkg.com/lang/en/docs/package-json/#toc-resolutions


          Кроме того, в рамках yarn можно массово слить дубликаты в yarn.lock, если их версии перекрываются — https://github.com/atlassian/yarn-deduplicate
          Вручную слить одиночные дубликаты(с перекрывающимися версиями) в рамках yarn.lock тоже не сложно

            0
            Если пользуетесь yarn'ом, то поле «resolutions» в package.json решит вашу проблему — yarnpkg.com/lang/en/docs/selective-version-resolutions.
            Для npm нативно кажется нет, но нашел вот такую штуку — github.com/rogeriochaves/npm-force-resolutions
              0
              Про то, как поправить дубликаты, есть отдельная глава Fixing bundle duplicates в документации inpectpack.

              Ведь дубликаты в «сторонних» пакетах

              Дубликаты вызываются сторонними пакетами, но есть шанс, что поправить это можно в своем package.json.

              В примере из статьи можно поменять версию lodash@4.1.0 на lodash@3.0.0, чтобы избавиться от дубликата:
              /* package.json */
              
              {
                /* ... */
                "name": "my-app",
                "dependencies": {
                  "lodash": "3.0.0", // 4.1.0 -> 3.0.0
                  "one": "1.2.3"
                }
              }
              


              Если дубликаты возникают из-за конфликтов версий пакетов в других пакетах или не хочется откатываться на старую версию, то можно попробовать resolutions в yarn'e или resolve.alias в webpack'e.

            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

            Самое читаемое