Плагины
Для общего ознакомления с написанием плагинов, начните с Как написать плагин.
Многие объекты в Webpack расширяют класс Tapable, который предоставляет метод plugin. А с помощью метода plugin, плагины могут вставлять пользовательские шаги сборки. Вы часто увидите compiler.plugin и compilation.plugin. По существу, каждый из этих вызовов плагина связывает обратный вызов, который срабатывает на определенных этапах процесса сборки.
Плагин устанавливается один раз при запуске Webpack. Webpack устанавливает плагин, вызывая его метод apply, и передаёт ссылку на объект Webpack compiler. Затем вы можете вызвать compiler.plugin, чтобы получить доступ к компиляциям ресурсов и их отдельным шагам сборки. Пример выглядит так:
// MyPlugin.js
function MyPlugin(options) {
// Configure your plugin with options...
}
MyPlugin.prototype.apply = function(compiler) {
compiler.plugin("compile", function(params) {
console.log("The compiler is starting to compile...");
});
compiler.plugin("compilation", function(compilation) {
console.log("The compiler is starting a new compilation...");
compilation.plugin("optimize", function() {
console.log("The compilation is starting to optimize files...");
});
});
compiler.plugin("emit", function(compilation, callback) {
console.log("The compilation is going to emit files...");
callback();
});
};
module.exports = MyPlugin;
Затем в webpack.config.js
plugins: [
new MyPlugin({options: 'nada'})
]
Примечание по типам интерфейсов…
Существует два типа интерфейсов плагинов.
-
Основанные на времени
- sync (по умолчанию): Как показано выше. Используйте return.
- async: Последний параметр — это обратный вызов. Подпись: function(err, result)
- parallel: Обработчики вызываются параллельно (async).
-
Значение возврата
- не прерывающий (по умолчанию): Значение возврата отсутствует.
- прерывающий: Обработчики вызываются по порядку, пока один из них не вернёт что-то.
- параллельный прерывающий: Обработчики вызываются параллельно (async). Первое возвращённое значение (по порядку) является значимым.
- waterfall: Каждый обработчик получает значение возврата предыдущего обработчика в качестве аргумента.
Экземпляр компилятора
Плагины должны иметь метод apply в своей цепочке прототипов (или связанный с ним), чтобы иметь доступ к экземпляру компилятора.
//MyPlugin.js
function MyPlugin() {};
MyPlugin.prototype.apply = function (compiler) {
//now you have access to all the compiler instance methods
}
module.exports = MyPlugin;
Что-то вроде этого тоже должно работать
//MyFunction.js
function apply(options, compiler) {
//now you have access to the compiler instance
//and options
}
//this little trick makes it easier to pass and check options to the plugin
module.exports = function(options) {
if (options instanceof Array) {
options = {
include: options
};
}
if (!Array.isArray(options.include)) {
options.include = [ options.include ];
}
return {
apply: apply.bind(this, options)
};
};
run(compiler: Compiler) асинхронный
Метод run компилятора используется для запуска компиляции. Этот метод не вызывается в режиме наблюдения.
watch-run(watching: Watching) асинхронный
Метод watch компилятора используется для запуска компиляции в режиме наблюдения. Этот метод не вызывается в обычном режиме.
compilation(c: Compilation, params: Object)
Создаётся Compilation. Плагин может использовать его, чтобы получить ссылку на объект Compilation. Объект params содержит полезные ссылки.
normal-module-factory(nmf: NormalModuleFactory)
Создаётся NormalModuleFactory. Плагин может использовать его, чтобы получить ссылку на объект NormalModuleFactory.
compiler.plugin("normal-module-factory", function(nmf) {
nmf.plugin("after-resolve", function(data) {
data.loaders.unshift(path.join(__dirname, "postloader.js"));
});
});
context-module-factory(cmf: ContextModuleFactory)
Создаётся ContextModuleFactory. Плагин может использовать его, чтобы получить ссылку на объект ContextModuleFactory.
compile(params)
Компилятор начинает компиляцию. Этот этап используется в обычном и наблюдаемом режимах. Плагины могут использовать этот момент для изменения объекта params (например, для оформления фабрик).
compiler.plugin("compile", function(params) {
//you are now in the "compile" phase
});
make(c: Compilation) параллельно
Плагины могут использовать этот момент для добавления записей в компиляцию или предварительной выборки модулей. Для этого они могут вызвать addEntry(context, entry, name, callback) или prefetch(context, dependency, callback) на компиляции.
after-compile(c: Compilation) асинхронный
Процесс компиляции завершен, и модули запечатаны. Следующий шаг — выдача сгенерированных данных. Здесь модули могут использовать результаты различными интересными способами.
Обработчики не копируются в дочерние компиляторы.
emit(c: Compilation) асинхронный
Компилятор начинает выдачу сгенерированных ресурсов. Здесь плагины имеют последнюю возможность добавить ресурсы в массив c.assets.
after-emit(c: Compilation) асинхронный
Компилятор выдал все ресурсы.
done(stats: Stats)
Всё завершено.
failed(err: Error)
Компилятор работает в режиме наблюдения и компиляция завершилась неудачно.
invalid()
Компилятор работает в режиме наблюдения и обнаружено изменение файла. Компиляция начнётся в ближайшее время (options.watchDelay).
after-plugins()
Все плагины, извлечённые из объекта параметров, добавляются в компилятор.
after-resolvers()
Все плагины, извлеченные из объекта опций, добавляются в разрешители.
Экземпляр компиляции
Экземпляр Compilation расширяет компилятор. т. е. compiler.compilation Он представляет собой фактическую компиляцию всех объектов в графе require. Этот объект имеет доступ ко всем модулям и их зависимостям (большая часть из которых является циклическими ссылками). На этапе компиляции модули загружаются, запечатываются, оптимизируются, группируются, хэшируются и восстанавливаются и т. д. Это был бы основной жизненный цикл любых операций компиляции.
compiler.plugin("compilation", function(compilation) {
//the main compilation instance
//all subsequent methods are derived from compilation.plugin
});
normal-module-loader
Обычный загрузчик модулей — это функция, которая фактически загружает все модули в графе модулей (по одному).
compilation.plugin('normal-module-loader', function(loaderContext, module) {
//this is where all the modules are loaded
//one by one, no dependencies are created yet
});
seal
Началась фаза запечатывания компиляции.
compilation.plugin('seal', function() {
//you are not accepting any more modules
//no arguments
});
optimize
Оптимизация компиляции.
compilation.plugin('optimize', function() {
//webpack is begining the optimization phase
// no arguments
});
optimize-tree(chunks, modules) асинхронный
Асинхронная оптимизация дерева.
compilation.plugin('optimize-tree', function(chunks, modules) {
});
optimize-modules(modules: Module[])
Оптимизация модулей.
compilation.plugin('optimize-modules', function(modules) {
//handle to the modules array during tree optimization
});
after-optimize-modules(modules: Module[])
Оптимизация модулей завершена.
optimize-chunks(chunks: Chunk[])
Оптимизация пакетов.
//optimize chunks may be run several times in a compilation
compilation.plugin('optimize-chunks', function(chunks) {
//unless you specified multiple entries in your config
//there's only one chunk at this point
chunks.forEach(function (chunk) {
//chunks have circular references to their modules
chunk.modules.forEach(function (module){
//module.loaders, module.rawRequest, module.dependencies, etc.
});
});
});
after-optimize-chunks(chunks: Chunk[])
Оптимизация пакетов завершена.
revive-modules(modules: Module[], records)
Восстановление информации о модулях из записей.
optimize-module-order(modules: Module[])
Сортировка модулей по важности. Первый — самый важный модуль. Он получит наименьший идентификатор.
optimize-module-ids(modules: Module[])
Оптимизация идентификаторов модулей.
after-optimize-module-ids(modules: Module[])
Оптимизация идентификаторов модулей завершена.
record-modules(modules: Module[], records)
Сохранение информации о модулях в записях.
revive-chunks(chunks: Chunk[], records)
Восстановление информации о пакетах из записей.
optimize-chunk-order(chunks: Chunk[])
Сортировка пакетов по важности. Первый — самый важный пакет. Он получит наименьший идентификатор.
optimize-chunk-ids(chunks: Chunk[])
Оптимизация идентификаторов пакетов.
after-optimize-chunk-ids(chunks: Chunk[])
Оптимизация идентификаторов пакетов завершена.
record-chunks(chunks: Chunk[], records)
Сохранение информации о пакетах в записях.
before-hash
Перед хэшированием компиляции.
after-hash
После хэширования компиляции.
before-chunk-assets
Перед созданием ресурсов пакета.
additional-chunk-assets(chunks: Chunk[])
Создание дополнительных ресурсов для пакетов.
record(compilation, records)
Сохранение информации о компиляции в записях.
optimize-chunk-assets(chunks: Chunk[]) асинхронный
Оптимизация ресурсов для пакетов.
Ресурсы хранятся в this.assets, но не все из них являются ресурсами пакетов. У Chunk есть свойство files, которое указывает на все файлы, созданные этим пакетом. Дополнительные ресурсы пакета хранятся в this.additionalChunkAssets.
Вот пример, который просто добавляет заголовок к каждому пакету.
compilation.plugin("optimize-chunk-assets", function(chunks, callback) {
chunks.forEach(function(chunk) {
chunk.files.forEach(function(file) {
compilation.assets[file] = new ConcatSource("\/**Sweet Banner**\/", "\n", compilation.assets[file]);
});
});
callback();
});
after-optimize-chunk-assets(chunks: Chunk[])
Ресурсы пакетов оптимизированы. Вот пример плагина от @boopathi, который выводит то, что вошло в каждый пакет.
var PrintChunksPlugin = function() {};
PrintChunksPlugin.prototype.apply = function(compiler) {
compiler.plugin('compilation', function(compilation, params) {
compilation.plugin('after-optimize-chunk-assets', function(chunks) {
console.log(chunks.map(function(c) {
return {
id: c.id,
name: c.name,
includes: c.modules.map(function(m) {
return m.request;
})
};
}));
});
});
};
optimize-assets(assets: Object{name: Source}) асинхронный
Оптимизация всех ресурсов.
Ресурсы хранятся в this.assets.
after-optimize-assets(assets: Object{name: Source})
Ресурсы оптимизированы.
build-module(module)
Перед началом сборки модуля.
compilation.plugin('build-module', function(module){
console.log('build module');
console.log(module);
});
succeed-module(module)
Модуль успешно собран.
compilation.plugin('succeed-module', function(module){
console.log('succeed module');
console.log(module);
});
failed-module(module)
Сборка модуля завершилась неудачно.
compilation.plugin('failed-module', function(module){
console.log('failed module');
console.log(module);
});
module-asset(module, filename)
Ресурс модуля добавлен в компиляцию.
chunk-asset(chunk, filename)
Ресурс пакета добавлен в компиляцию.
Экземпляр MainTemplate
startup(source, module, hash)
compilation.mainTemplate.plugin('startup', function(source, module, hash) {
if (!module.chunks.length && source.indexOf('__ReactStyle__') === -1) {
var originName = module.origins && module.origins.length ? module.origins[0].name : 'main';
return ['if (typeof window !== "undefined") {',
' window.__ReactStyle__ = ' + JSON.stringify(classNames[originName]) + ';',
'}'
].join('\n') + source;
}
return source;
});
Экземпляр Parser (compiler.parser)
Экземпляр парсера принимает строку и обратный вызов и вернёт выражение при совпадении.
compiler.parser.plugin("var rewire", function (expr) {
//if you original module has 'var rewire'
//you now have a handle on the expresssion object
return true;
});
program(ast) прерывающий
Плагин общего назначения для абстрактного синтаксического дерева (AST) фрагмента кода.
statement(statement: Statement) прерывающий
Плагин общего назначения для инструкций фрагмента кода.
call <identifier>(expr: Expression) прерывающий
abc(1) => call abc
a.b.c(1) => call a.b.c
expression <identifier>(expr: Expression) прерывающий
abc => expression abc
a.b.c => expression a.b.c
expression ?:(expr: Expression) прерывающий
(abc ? 1 : 2) => expression ?!
Возвращает логическое значение, чтобы пропустить разбор неправильного пути.
typeof <identifier>(expr: Expression) прерывающий
typeof a.b.c => typeof a.b.c
statement if(statement: Statement) прерывающий
if(abc) {} => statement if
Возвращает логическое значение, чтобы пропустить разбор неправильного пути.
label <labelname>(statement: Statement) прерывающий
xyz: abc => label xyz
var <name>(statement: Statement) прерывающий
var abc, def => var abc + var def
Возвращает false, чтобы не добавлять переменную в известные определения.
evaluate <expression type>(expr: Expression) прерывающий
Оценить выражение.
evaluate typeof <identifier>(expr: Expression) прерывающий
Оценить тип идентификатора.
evaluate Identifier <identifier>(expr: Expression) прерывающий
Оценить идентификатор, являющийся свободной переменной.
evaluate defined Identifier <identifier>(expr: Expression) отмена
Оценить идентификатор, являющийся определённой переменной.
evaluate CallExpression .<property>(expr: Expression) отмена
Оценить вызов члена функции успешно оценённого выражения.
Фабрика модулей NormalModuleFactory
before-resolve(data) асинхронная каскадная обработка
Перед тем, как фабрика начнёт разрешение. Объект data имеет следующие свойства:
-
contextАбсолютный путь к каталогу для разрешения. -
requestЗапрос выражения.
Плагины могут изменять объект или передать новый похожий объект в коллбэк.
after-resolve(data) асинхронная каскадная обработка
После того, как фабрика выполнила разрешение запроса. Объект data имеет следующие свойства:
-
requestРазрешённый запрос. Он служит идентификатором для NormalModule. -
userRequestЗапрос, введённый пользователем. Он разрешён, но не содержит пре- и пост-загрузчиков. -
rawRequestНеразрешённый запрос. -
loadersМассив разрешённых загрузчиков. Он передаётся NormalModule, и они будут выполнены. -
resourceРесурс. Он будет загружен NormalModule. -
parserПарсер, который будет использован NormalModule.
Фабрика модулей ContextModuleFactory
before-resolve(data) асинхронная каскадная обработка
after-resolve(data) асинхронная каскадная обработка
alternatives(options: Array) асинхронная каскадная обработка
Разрешители
-
compiler.resolvers.normalРазрешитель для обычного модуля -
compiler.resolvers.contextРазрешитель для модуля контекста -
compiler.resolvers.loaderРазрешитель для загрузчика
Любой плагин должен использовать this.fileSystem в качестве fileSystem, поскольку он кешируется. Он содержит только асинхронные именованные функции, но они могут вести себя синхронно, если пользователь использует синхронную реализацию файловой системы (например, в enhanced-require).
Для объединения путей любой плагин должен использовать this.join. Он нормализует пути. Также есть this.normalize.
Доступна асинхронная реализация forEach для отмены this.forEachBail(array, iterator, callback).
Для передачи запроса другим плагинам разрешения используйте метод this.doResolve(types: String|String[], request: Request, callback) . types — это несколько возможных типов запросов, которые тестируются в порядке предпочтения.
interface Request {
path: String // The current directory of the request
request: String // The current request string
query: String // The query string of the request, if any
module: boolean // The request begins with a module
directory: boolean // The request points to a directory
file: boolean // The request points to a file
resolved: boolean // The request is resolved/done
// undefined means false for boolean fields
}
// Examples
// from /home/user/project/file.js: require("../test?charset=ascii")
{
path: "/home/user/project",
request: "../test",
query: "?charset=ascii"
}
// from /home/user/project/file.js: require("test/test/")
{
path: "/home/user/project",
request: "test/test/",
module: true,
directory: true
}
resolve(context: String, request: String)
Перед началом процесса разрешения.
resolve-step(types: String[], request: Request)
Перед началом отдельного шага в процессе разрешения.
module(request: Request) асинхронная каскадная обработка
Найден запрос на модуль и должен быть разрешён.
directory(request: Request) асинхронная каскадная обработка
Найден запрос на каталог и должен быть разрешён.
file(request: Request) асинхронная каскадная обработка
Найден запрос на файл и должен быть разрешён.
Плагины могут предложить дополнительные точки расширения
Вот список того, что предлагают стандартные плагины в webpack. Все они — (request: Request) асинхронная каскадная обработка.
Процесс для обычных модулей и контекстов — module -> module-module -> directory -> file.
Процесс для загрузчиков — module -> module-loader-module -> module-module -> directory -> file.
module-module
Модуль должен быть найден в указанном каталоге. path содержит каталог.
module-loader-module (только для загрузчиков)
Используется перед применением шаблонов модулей к имени модуля. Процесс продолжается с module-module.
© 2012–2015 Tobias Koppers
Licensed under the MIT License.
https://webpack.github.io/docs/plugins.html