Spec-Zone.ru › webpack 5

Интерфейс загрузчика

Загрузчик — это JavaScript-модуль, который экспортирует функцию. Загрузчик вызывается загрузчиком и получает на вход результат предыдущего загрузчика или ресурсный файл. Контекст функции заполняется webpack и загрузчиком с полезными методами, позволяющими, среди прочего, изменить стиль вызова на асинхронный или получить параметры запроса.

Первый загрузчик получает один аргумент: содержимое ресурсного файла. Компилятор ожидает результат от последнего загрузчика. Результат должен быть String или Buffer (который преобразуется в строку), представляющий JavaScript-код модуля. Также может быть передан необязательный результат SourceMap (в виде JSON-объекта).

Единственный результат может быть возвращён в синхронном режиме. Для нескольких результатов необходимо вызвать this.callback(). В асинхронном режиме необходимо вызвать this.async(), чтобы указать загрузчику ожидать асинхронного результата. Возвращается this.callback(). Затем загрузчик должен вернуть undefined и вызвать этот коллбэк.

/**
 *
 * @param {string|Buffer} content Content of the resource file
 * @param {object} [map] SourceMap data consumable by https://github.com/mozilla/source-map
 * @param {any} [meta] Meta data, could be anything
 */
function webpackLoader(content, map, meta) {
  // code of your webpack loader
}

Примеры

В следующих разделах представлены некоторые базовые примеры различных типов загрузчиков. Обратите внимание, что параметры map и meta являются необязательными, см. this.callback ниже.

Синхронные загрузчики

Для синхронного возврата преобразованного content можно использовать return или this.callback.

sync-loader.js

module.exports = function (content, map, meta) {
  return someSyncOperation(content);
};

Метод this.callback более гибкий, так как вы передаёте несколько аргументов вместо использования только content.

sync-loader-with-multiple-results.js

module.exports = function (content, map, meta) {
  this.callback(null, someSyncOperation(content), map, meta);
  return; // always return undefined when calling callback()
};

Асинхронные загрузчики

Для асинхронных загрузчиков используется this.async для получения callback функции:

async-loader.js

module.exports = function (content, map, meta) {
  var callback = this.async();
  someAsyncOperation(content, function (err, result) {
    if (err) return callback(err);
    callback(null, result, map, meta);
  });
};

async-loader-with-multiple-results.js

module.exports = function (content, map, meta) {
  var callback = this.async();
  someAsyncOperation(content, function (err, result, sourceMaps, meta) {
    if (err) return callback(err);
    callback(null, result, sourceMaps, meta);
  });
};
подсказка

Загрузчики изначально были разработаны для работы в синхронных цепочках загрузчиков, таких как Node.js (с использованием enhanced-require), и асинхронных цепочках, как в webpack. Однако, так как дорогостоящие синхронные вычисления — плохая идея в однопоточной среде, подобной Node.js, мы рекомендуем делать свой загрузчик асинхронным, если это возможно. Синхронные загрузчики допустимы, если объём вычислений незначителен.

"Сырой" загрузчик

По умолчанию, ресурсный файл преобразуется в строку UTF-8 и передаётся загрузчику. Установив флаг raw в значение true, загрузчик получит сырой Buffer. Каждый загрузчик может передавать результат как String или как Buffer. Компилятор выполняет преобразования между загрузчиками.

raw-loader.js

module.exports = function (content) {
  assert(content instanceof Buffer);
  return someSyncOperation(content);
  // return value can be a `Buffer` too
  // This is also allowed if loader is not "raw"
};
module.exports.raw = true;

Фаза предварительной обработки загрузчика

Загрузчики всегда вызываются справа налево. В некоторых случаях загрузчику требуется только метаданные запроса, и он может игнорировать результаты предыдущего загрузчика. Метод pitch загрузчиков вызывается слева направо перед фактическим выполнением загрузчиков (справа налево).

подсказка

Загрузчики могут быть добавлены в запросы непосредственно и отключены с помощью префиксов, что повлияет на порядок «предварительной обработки» и выполнения. Подробнее см. Rule.enforce.

Для следующей конфигурации use:

module.exports = {
  //...
  module: {
    rules: [
      {
        //...
        use: ['a-loader', 'b-loader', 'c-loader'],
      },
    ],
  },
};

Произойдут эти шаги:

|- a-loader `pitch`
  |- b-loader `pitch`
    |- c-loader `pitch`
      |- requested module is picked up as a dependency
    |- c-loader normal execution
  |- b-loader normal execution
|- a-loader normal execution

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

Во-первых, data, переданное в метод pitch, доступно и на стадии выполнения, как this.data, и может быть полезно для захвата и обмена информацией на более ранних этапах цикла.

module.exports = function (content) {
  return someSyncOperation(content, this.data.value);
};

module.exports.pitch = function (remainingRequest, precedingRequest, data) {
  data.value = 42;
};

Во-вторых, если загрузчик возвращает результат в методе pitch, процесс разворачивается, и оставшиеся загрузчики пропускаются. В приведённом выше примере, если метод b-loader загрузчика pitch вернул что-то:

module.exports = function (content) {
  return someSyncOperation(content);
};

module.exports.pitch = function (remainingRequest, precedingRequest, data) {
  if (someCondition()) {
    return (
      'module.exports = require(' +
      JSON.stringify('-!' + remainingRequest) +
      ');'
    );
  }
};

Вышеуказанные шаги были бы сокращены до:

|- a-loader `pitch`
  |- b-loader `pitch` returns a module
|- a-loader normal execution

Контекст загрузчика

Контекст загрузчика представляет собой свойства, доступные внутри загрузчика, присвоенные свойству this.

Пример для контекста загрузчика

Рассмотрим следующий пример, в котором используется вызов require:

В /abc/file.js:

require('./loader1?xyz!loader2!./resource?rrr');

this.addContextDependency

addContextDependency(directory: string)

Добавить директорию в качестве зависимости результата загрузчика.

this.addDependency

addDependency(file: string)
dependency(file: string) // shortcut

Добавить существующий файл в качестве зависимости результата загрузчика, чтобы сделать его наблюдаемым. Например, sass-loader, less-loader используют это для перекомпиляции при изменении любого импортированного css файла.

this.addMissingDependency

addMissingDependency(file: string)

Добавить несуществующий файл в качестве зависимости результата загрузчика, чтобы сделать его наблюдаемым. Аналогично addDependency, но обрабатывает создание файлов во время компиляции перед корректной привязкой наблюдателей.

this.async

Указывает загрузчику на асинхронный вызов коллбэка. Возвращает this.callback.

this.cacheable

Функция, которая устанавливает флаг кэшируемости:

cacheable(flag = true: boolean)

По умолчанию, результаты загрузчиков помечаются как кэшируемые. Вызовите этот метод, передав false, чтобы результат загрузчика не был кэшируемым.

Кэшируемый загрузчик должен иметь детерминированный результат, когда входные данные и зависимости не изменились. Это означает, что у загрузчика не должно быть зависимостей, кроме тех, что указаны с помощью this.addDependency.

this.callback

Функция, которая может быть вызвана синхронно или асинхронно для возврата нескольких результатов. Ожидаемые аргументы:

this.callback(
  err: Error | null,
  content: string | Buffer,
  sourceMap?: SourceMap,
  meta?: any
);
  1. Первый аргумент должен быть Error или null
  2. Второй аргумент — string или Buffer.
  3. Необязательно: третий аргумент — карта исходных данных, парсируемая модулем данного модуля.
  4. Необязательно: четвёртый параметр, игнорируемый webpack, может быть любым (например, некоторыми метаданными).
подсказка

Полезно передать абстрактное синтаксическое дерево (AST), как ESTree, в качестве четвёртого аргумента (meta), чтобы ускорить сборку, если вы хотите использовать общие AST между загрузчиками.

В случае вызова этой функции, вы должны вернуть undefined, чтобы избежать неоднозначных результатов загрузчика.

this.clearDependencies

clearDependencies();

Удалить все зависимости результата загрузчика, включая начальные и те, что принадлежат другим загрузчикам. Рассмотрите использование pitch.

this.context

Директория модуля. Может использоваться в качестве контекста для разрешения других вещей.

В примере: /abc, потому что resource.js находится в этой директории

this.data

Объект данных, используемый для обмена между фазой предварительной обработки и обычной фазой.

this.emitError

emitError(error: Error)

Вывести ошибку, которая также может быть отображена в выводе.

ERROR in ./src/lib.js (./src/loader.js!./src/lib.js)
Module Error (from ./src/loader.js):
Here is an Error!
 @ ./src/index.js 1:0-25
подсказка

В отличие от прямого выброса ошибки, это НЕ прервёт процесс компиляции текущего модуля.

this.emitFile

emitFile(name: string, content: Buffer|string, sourceMap: {...})

Вывести файл. Это специфично для webpack.

this.emitWarning

emitWarning(warning: Error)

Вывести предупреждение, которое будет отображено в выводе, как показано ниже:

WARNING in ./src/lib.js (./src/loader.js!./src/lib.js)
Module Warning (from ./src/loader.js):
Here is a Warning!
 @ ./src/index.js 1:0-25
подсказка

Обратите внимание, что предупреждения не будут отображены, если stats.warnings установлено в false, или используется другое значение для пропуска, например, none или errors-only. См. конфигурацию предустановок stats.

this.environment

Проверить какие типы ES-функциональности можно использовать в сгенерированном коде.

Например,

{
  // The environment supports arrow functions ('() => { ... }').
  "arrowFunction": true,
  // The environment supports BigInt as literal (123n).
  "bigIntLiteral": false,
  // The environment supports const and let for variable declarations.
  "const": true,
  // The environment supports destructuring ('{ a, b } = obj').
  "destructuring": true,
  // The environment supports an async import() function to import EcmaScript modules.
  "dynamicImport": false,
  // The environment supports an async import() when creating a worker, only for web targets at the moment.
  "dynamicImportInWorker": false,
  // The environment supports 'for of' iteration ('for (const x of array) { ... }').
  "forOf": true,
  // The environment supports 'globalThis'.
  "globalThis": true,
  // The environment supports ECMAScript Module syntax to import ECMAScript modules (import ... from '...').
  "module": false,
  // The environment supports optional chaining ('obj?.a' or 'obj?.()').
  "optionalChaining": true,
  // The environment supports template literals.
  "templateLiteral": true
}

this.fs

Доступ к свойству compilation's inputFileSystem.

this.getOptions(schema)

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

подсказка

С webpack 5 доступен this.getOptions. Он заменяет метод getOptions из loader-utils.

this.getResolve

getResolve(options: ResolveOptions): resolve

resolve(context: string, request: string, callback: function(err, result: string))
resolve(context: string, request: string): Promise<string>

Создаёт функцию разрешения, аналогичную this.resolve.

Любые параметры в resolve опциях webpack возможны. Они объединяются с настроенными resolve опциями. Обратите внимание, что "..." может использоваться в массивах для расширения значения из resolve опций, например, { extensions: [".sass", "..."] }.

options.dependencyType — это дополнительная опция. Она позволяет указать тип зависимости, используемый для разрешения byDependency из resolve опций.

Все зависимости операции разрешения автоматически добавляются в зависимости текущего модуля.

this.hot

Информация о HMR для загрузчиков.

module.exports = function (source) {
  console.log(this.hot); // true if HMR is enabled via --hot flag or webpack configuration
  return source;
};

this.hashDigest

string

5.95.0+

Кодировка для генерации хэша. См. output.hashDigest.

this.hashDigestLength

number

5.95.0+

Длина префикса хэша для использования. См. output.hashDigestLength.

this.hashFunction

string function

5.95.0+

Алгоритм хеширования. См. output.hashFunction.

this.hashSalt

string

5.95.0+

Необязательная соль для обновления хэша через Node.JS' hash.update. См. output.hashSalt.

this.importModule

5.32.0+

this.importModule(request, options, [callback]): Promise

Альтернативное лёгкое решение для дочернего компилятора для компиляции и выполнения запроса во время сборки.

  • request: строка запроса для загрузки модуля
  • options:
    • layer: укажите слой, в котором этот модуль размещён/скомпилирован
    • publicPath: общедоступный путь, используемый для скомпилированных модулей
  • callback: необязательный обратный вызов в стиле Node.js, возвращающий экспорт модуля или объект пространства имён для ESM. importModule вернёт Promise, если обратный вызов не предоставлен.

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /stylesheet\.js$/i,
        use: ['./a-pitching-loader.js'],
        type: 'asset/source', // we set type to 'asset/source' as the loader will return a string
      },
    ],
  },
};

a-pitching-loader.js

exports.pitch = async function (remaining) {
  const result = await this.importModule(
    this.resourcePath + '.webpack[javascript/auto]' + '!=!' + remaining
  );
  return result.default || result;
};

src/stylesheet.js

import { green, red } from './colors.js';
export default `body { background: ${red}; color: ${green}; }`;

src/colors.js

export const red = '#f00';
export const green = '#0f0';

src/index.js

import stylesheet from './stylesheet.js';
// stylesheet will be a string `body { background: #f00; color: #0f0; }` at build time

Вы, возможно, заметите кое-что в приведённом выше примере:

  1. У нас есть загрузчик-помощник,
  2. Мы используем синтаксис !=! в этом загрузчике-помощнике, чтобы установить matchResource для запроса, т.е. мы будем использовать this.resourcePath + '.webpack[javascript/auto]' для соответствия с module.rules, а не с исходным ресурсом,
  3. .webpack[javascript/auto] — псевдорасширение шаблона .webpack[type], мы используем его для указания типа модуля по умолчанию, когда не указан другой тип модуля. Обычно он используется совместно с синтаксисом !=!.

Обратите внимание, что вышеприведённый пример упрощённый. Вы можете ознакомиться с полным примером на репозитории webpack.

this.loaderIndex

Индекс в массиве загрузчиков текущего загрузчика.

В примере: в loader1: 0, в loader2: 1

this.loadModule

loadModule(request: string, callback: function(err, source, sourceMap, module))

Разрешает указанный запрос до модуля, применяет все настроенные загрузчики и возвращает сгенерированный исходный код, sourceMap и экземпляр модуля (обычно экземпляр NormalModule). Используйте эту функцию, если вам нужно узнать исходный код другого модуля для генерации результата.

this.loadModule в контексте загрузчика по умолчанию использует правила разрешения CommonJS. Используйте this.getResolve с соответствующим dependencyType, например, 'esm', 'commonjs' или настроенным, прежде чем использовать другую семантику.

this.loaders

Массив всех загрузчиков. Он изменяется на фазе pitch.

loaders = [{request: string, path: string, query: string, module: function}]

В примере:

[
  {
    request: '/abc/loader1.js?xyz',
    path: '/abc/loader1.js',
    query: '?xyz',
    module: [Function],
  },
  {
    request: '/abc/node_modules/loader2/index.js',
    path: '/abc/node_modules/loader2/index.js',
    query: '',
    module: [Function],
  },
];

this.mode

Считывает, в каком mode режиме работает webpack.

Возможные значения: 'production', 'development', 'none'

this.query

  1. Если загрузчик был настроен с объектом options, он будет указывать на этот объект.
  2. Если у загрузчика нет options, но он был вызван со строкой запроса, это будет строка, начинающаяся с ?.

this.request

Разрешённая строка запроса.

В примере: '/abc/loader1.js?xyz!/abc/node_modules/loader2/index.js!/abc/resource.js?rrr'

this.resolve

resolve(context: string, request: string, callback: function(err, result: string))

Разрешает запрос, подобно выражению require.

  • context должен быть абсолютным путём к директории. Эта директория используется в качестве начальной точки для разрешения.
  • request — запрос для разрешения. Обычно используются относительные запросы, такие как ./relative, или запросы к модулям, такие как module/path, но также возможны абсолютные пути, такие как /some/path, в качестве запросов.
  • callback — обычная функция обратного вызова в стиле Node.js, возвращающая разрешённый путь.

Все зависимости операции разрешения автоматически добавляются в зависимости текущего модуля.

this.resource

Часть запроса, относящаяся к ресурсу, включая запрос.

В примере: '/abc/resource.js?rrr'

this.resourcePath

Файл ресурса.

В примере: '/abc/resource.js'

this.resourceQuery

Запрос к ресурсу.

В примере: '?rrr'

this.rootContext

Начиная с webpack 4, ранее this.options.context предоставляется как this.rootContext.

this.sourceMap

Указывает, нужно ли генерировать source map. Поскольку генерация source map может быть ресурсоёмкой задачей, необходимо проверить, действительно ли source map запрошен.

this.target

Целевая среда компиляции. Передаётся из конфигурационных параметров.

Примеры значений: 'web', 'node'

this.utils

5.27.0+

Доступ к следующим утилитам.

  • absolutify: Возвращает новую строку запроса, используя абсолютные пути, где это возможно.
  • contextify: Возвращает новую строку запроса, избегая абсолютных путей, где это возможно.
  • createHash: Возвращает новый объект Hash из предоставленной функции хеширования.

my-sync-loader.js

module.exports = function (content) {
  this.utils.contextify(
    this.context,
    this.utils.absolutify(this.context, './index.js')
  );
  this.utils.absolutify(this.context, this.resourcePath);
  const mainHash = this.utils.createHash(
    this._compilation.outputOptions.hashFunction
  );
  mainHash.update(content);
  mainHash.digest('hex');
  // …
  return content;
};

this.version

Версия API загрузчика. В настоящее время 2. Это полезно для обеспечения обратной совместимости. С помощью версии можно указать специальную логику или fallback для изменений, нарушающих обратную совместимость.

this.webpack

Этот флаг устанавливается в true, когда он скомпилирован webpack.

Подсказка

Загрузчики изначально были разработаны для работы и как преобразования Babel. Поэтому, если вы пишете загрузчик, который работает и в том, и в другом случае, вы можете использовать этот параметр, чтобы узнать, есть ли доступ к дополнительным функциям loaderContext и webpack.

Свойства, специфичные для Webpack

Интерфейс загрузчика предоставляет всю информацию, связанную с модулем. Однако в редких случаях вам может потребоваться доступ к API самого компилятора.

Предупреждение

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

Поэтому следует использовать их только в крайнем случае. Их использование снизит переносимость вашего загрузчика.

this._compilation

Доступ к текущему объекту Compilation webpack.

this._compiler

Доступ к текущему объекту Compiler webpack.

Устаревшие свойства контекста

Предупреждение

Использование этих свойств крайне не рекомендуется, так как мы планируем их удалить из контекста. Они всё ещё указаны здесь для целей документации.

this.debug

Флаг булевого типа. Он устанавливается при включении режима отладки.

this.inputValue

Передаётся от последнего загрузчика. Если вы хотели бы выполнить входной аргумент как модуль, рассмотрите чтение этой переменной для сокращения (для производительности).

this.minimize

Указывает, нужно ли минимизировать результат.

this.value

Передаёт значения следующему загрузчику. Если вы знаете, что экспортирует ваш результат, если он выполняется как модуль, установите это значение здесь (как единственный элемент массива).

this._module

Доступ к объекту Module, который загружается.

Обработка ошибок

Вы можете сообщать об ошибках изнутри загрузчика, используя:

  • Функцию this.emitError. Сообщит об ошибках без прерывания компиляции модуля.
  • Выброс исключения throw (или другого неперехваченного исключения). Выбрасывание ошибки во время работы загрузчика приведёт к ошибке компиляции текущего модуля.
  • Передачу ошибки в обратный вызов callback (в асинхронном режиме). Передача ошибки в обратный вызов также приведёт к ошибке компиляции модуля.

Например:

./src/index.js

require('./loader!./lib');

Выбрасывание ошибки из загрузчика:

./src/loader.js

module.exports = function (source) {
  throw new Error('This is a Fatal Error!');
};

Или передача ошибки в обратный вызов в асинхронном режиме:

./src/loader.js

module.exports = function (source) {
  const callback = this.async();
  //...
  callback(new Error('This is a Fatal Error!'), source);
};

Модуль будет собран следующим образом:

/***/ "./src/loader.js!./src/lib.js":
/*!************************************!*\
  !*** ./src/loader.js!./src/lib.js ***!
  \************************************/
/*! no static exports found */
/***/ (function(module, exports) {

throw new Error("Module build failed (from ./src/loader.js):\nError: This is a Fatal Error!\n    at Object.module.exports (/workspace/src/loader.js:3:9)");

/***/ })

Затем вывод сборки также отобразит ошибку (аналогично this.emitError):

ERROR in ./src/lib.js (./src/loader.js!./src/lib.js)
Module build failed (from ./src/loader.js):
Error: This is a Fatal Error!
    at Object.module.exports (/workspace/src/loader.js:2:9)
 @ ./src/index.js 1:0-25

Как вы можете видеть ниже, отображается не только сообщение об ошибке, но и подробности о том, какой загрузчик и модуль вовлечены:

  • путь к модулю: ERROR in ./src/lib.js
  • строка запроса: (./src/loader.js!./src/lib.js)
  • путь к загрузчику: (from ./src/loader.js)
  • путь вызывающего объекта: @ ./src/index.js 1:0-25
Предупреждение

Путь к загрузчику в сообщении об ошибке отображается начиная с webpack 4.12

Подсказка

Все ошибки и предупреждения будут записаны в stats. См. Данные о состоянии.

Встроенное соответствие matchResource

В webpack v4 был введён новый синтаксис встраиваемых запросов. Представление <match-resource>!=! перед запросом установит matchResource для этого запроса.

Предупреждение

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

Подсказка

Относительный matchResource будет разрешаться относительно текущего контекста содержащего модуля.

При установке matchResource, он будет использоваться для соответствия с module.rules, а не с исходным ресурсом. Это может быть полезно, если последующие загрузчики должны применяться к ресурсу или если тип модуля необходимо изменить. Это также отображается в статистике и используется для сопоставления Rule.issuer и test в splitChunks.

Пример:

file.js

/* STYLE: body { background: red; } */
console.log('yep');

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

file.js (преобразованный загрузчиком)

import './file.js.css!=!extract-style-loader/getStyles!./file.js';
console.log('yep');

Это добавит зависимость к extract-style-loader/getStyles!./file.js и обработает результат как file.js.css. Поскольку module.rules содержит правило, соответствующее /\.css$/, оно будет применено к этой зависимости.

Загрузчик может выглядеть так:

extract-style-loader/index.js

const getStylesLoader = require.resolve('./getStyles');

module.exports = function (source) {
  if (STYLES_REGEXP.test(source)) {
    source = source.replace(STYLES_REGEXP, '');
    return `import ${JSON.stringify(
      this.utils.contextify(
        this.context || this.rootContext,
        `${this.resource}.css!=!${getStylesLoader}!${this.remainingRequest}`
      )
    )};${source}`;
  }
  return source;
};

extract-style-loader/getStyles.js

module.exports = function (source) {
  const match = source.match(STYLES_REGEXP);
  return match[0];
};

Ведение журнала

API ведения журнала доступен с выпуска webpack 4.37. Когда logging включено в stats configuration и/или когда infrastructure logging включено, загрузчики могут записывать сообщения, которые будут выведены в соответствующем формате логгера (статистика, инфраструктура).

  • Загрузчики должны отдавать предпочтение использованию this.getLogger() для ведения журналов, что является сокращением от compilation.getLogger() с путем загрузчика и обработанным файлом. Этот тип ведения журналов сохраняется в статистике и форматируется соответственно. Пользователь webpack может его фильтровать и экспортировать.
  • Загрузчики могут использовать this.getLogger('name') для получения независимого логгера с именем дочернего элемента. Путь загрузчика и обработанный файл по-прежнему добавляются.
  • Загрузчики могут использовать специальную логику обратного вызова для определения поддержки ведения журналов this.getLogger ? this.getLogger() : console для обеспечения обратного вызова, когда используется более старая версия webpack, не поддерживающая метод getLogger.

© JS Foundation and other contributors
Licensed under the Creative Commons Attribution License 4.0.
https://webpack.js.org/api/loaders

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API