Pesquisa de site

javascript - Plugin do Webpack: Como adicionar dinamicamente um loader a um módulo?


Estou escrevendo um plugin para Webpack. Durante a resolução de módulos, este plugin deve adicionar dinamicamente um loader específico para alguns módulos, mas não para todos.

Minha ideia agora é aproveitar o evento after-resolve do normal-module-factory. Ali, eu poderia modificar o array data.loaders. Alguns testes de depuração me mostraram que esse array contém objetos da forma { loader: string, options: object | undefined }.

Não encontrei nenhuma documentação sobre como modificar carregadores por módulo dinamicamente. Então estou me perguntando:

  • Esta é a abordagem certa?
  • Algumas (mas não todas) entradas de loaders existentes contêm uma propriedade adicional ident, parecendo assim: 'ref--0-0'. Quando devo definir essa propriedade e com qual valor?

Resposta #1

Não tenho certeza se é a melhor solução, mas este código parece funcionar:

class MyPlugin {
  apply(compiler) {
    compiler.plugin('normal-module-factory', normalModuleFactory => {
      normalModuleFactory.plugin('after-resolve', (data, callback) => {
        data.loaders.push({
          loader: ..., // Path to loader
          options: {}
        });
        callback(null, data);
      });
    });
  }
}

module.exports = MyPlugin;

Sobre a propriedade extra ident: o Webpack precisa ser capaz de comparar as opções do loader. Por padrão, ele faz isso chamando JSON.stringify() no objeto de opções e, em seguida, comparando as strings resultantes. Ao adicionar uma propriedade ident, você pode especificar explicitamente uma representação em string do objeto de opções. Para mais detalhes, veja https://stackoverflow.com/a/49006233/52041.

Resposta #2

Eu estava tentando resolver um problema semelhante e queria adicionar um carregador apenas para arquivos específicos identificados em outro lugar em um plugin. Minha solução é a seguinte:

loader.js

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

plugin.js

const path = require('path');

class MyPluginName {
  apply(compiler) {
    compiler.hooks.compilation.tap('MyPluginName', compilation => {
      compilation.hooks.normalModuleLoader.tap('MyPluginName', (loaderContext, module) => {
        if (myModuleCondition(module)) {
          module.loaders.unshift({
            loader: path.resolve(__dirname, 'loader.js'), // Path to loader
            options: {}
          });
        }
      });
    });
  }
}

Observe que eu usei unshift em vez de push para adicionar o loader - ainda estou na fase de desenvolvimento, então posso estar errado, mas meu entendimento atual é que eu gostaria que meu loader fosse executado depois de todos os loaders 'padrão', que podem muito bem estar transpilando TypeScript, etc. Acredito que os loaders são então aplicados da direita para a esquerda. Dependendo do que você está fazendo, isso pode não ser importante.