Kristiyan Kostadinov
Kristiyan Kostadinov

Reputation: 3712

Adding new file dependency inside custom Webpack plugin

I'm writing a custom Webpack plugin that is supposed to modify some TypeScript source files and as a result, the dependencies between the files can change (e.g. an import to a file can be added or removed, as well as an import can be replaced by another import). I'm having issues with Webpack not picking up the new relationships between the files. The best I've got so far is that I can get it to pick up on some of the file changes, but it appears to be too late in the compilation's lifecycle and as a result the file content could be outdated. Here's a simplification of what I've got so far. I am confident that the logic that determines the newFiles and hasDependencyOnFile is correct:

class CustomWebpackPlugin {
  apply(compiler) {
    compiler.plugin('after-compile', (compilation, callback) => {
      compilation.fileDependencies.push(...newFiles);

      compilation.modules.forEach(module => {
        if (hasDependencyOnFile(module.resource)) {
          module.fileDependencies.push(...newFiles);
        }
      });

      callback();
    });
  }
}

Is this the proper way to approach adding new entries to the dependency graph or does Webpack provide an API to do it correctly?

Upvotes: 1

Views: 1568

Answers (2)

Sheng
Sheng

Reputation: 834

I have a similar problem to figure out recently. I tried injecting dependencies during compilation stage, but there are other dependencies and contexts I was not able to figure out soon. So instead I just update the source code by adding a new loader. In this loader, I use a JavaScript parser to get the information I need, then a prefix of import/require statements get created. This prefix gets added to the beginning of my legacy code, which doesn't have any of this kind of import/require dependencies.

Following is the sample code of the Webpack loader.

Using import statement will enforce strict mode in the compiled JavaScript. Using require will not enable strict mode, which is most likely helpful for legacy code.

module.exports = function(source) {
  // parseDependencies is a function created to parse the source code and create the right import file path based on the file path level input parameter.

  let filePath = this._module.userRequest;
  let subPath = filePath.substring(process.cwd().length, filePath.length)
  let level = (subPath.match(/\//g) || []).length - 5;

  let importList = parseDependencies(source, level).map(f => {
    ...
  });
  return importList.join("\r\n") + source;
}

Upvotes: 1

felixmosh
felixmosh

Reputation: 35573

It is not enough to add a file as a dependency of other file, you need that the "other file" will require your added file, therefore, my suggestion is to use normal-module-loader phase in order to add your custom loader that will add a require to you newFile, then webpack will add that newFile as a current dep.

Sample code:

compilation.plugin('normal-module-loader', (loaderContext, module) => {
    // this will be called for every successfully built module, but before it's parsed and
    // its dependencies are built. The built source is available as module._source.source()
    if (...some condition on the module) {
      module.loaders.push({loader: compiler.context+'/path/to/custom-loader.js'});
    }
});

Upvotes: 0

Related Questions