如何编写一个依赖于另一个模块中的某个值但仍然输出所有模块的加载器

时间:2017-11-10 19:12:20

标签: webpack

(我正在使用Webpack 3.8.1)

我有一个包含此问题所示代码的回购:

https://github.com/leoasis/webpack-loader-issue

我正在尝试编写一个webpack加载器来处理源代码的内容并生成一些值。如果源依赖于其他资源,我需要在加载器中加载它们来计算该值,因为它取决于依赖项的值。为此,我使用this.loadModule并在加载模块后立即访问该值。

然而,当我有依赖关系的依赖时,这似乎失败了,因为它们似乎没有包含在最终的包中,导致错误。

以下是重现案例的缩小示例:

// webpack.config.js

const path = require('path');

module.exports = {
  entry: './a.js',
  output: {
    filename: 'bundle.js'
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        use: './foo-loader'
      }
    ]
  }
};

// a.js
import './b';
console.log('Hello from a');


// b.js
import './c';
console.log('Hello from b');


// c.js
console.log('Hello from c');

最后,加载器:

// foo-loader.js

module.exports = function (content) {
  this.async();
  const dep = content.split('\n')[0].split('\'')[1];
  this._module.someValue = dep.charCodeAt(2); // gets the 'a' from './a'

  if (dep[0] !== '.') {
    this.callback(null, content + 'console.log("the value is", ' + this._module.someValue + ')');
    return;
  }

  this.loadModule(dep, (err, source, sourceMap, module) => {
    if (err) {
      this.callback(err);
      return;
    }

    this._module.someValue += module.someValue;
    console.log(this.resourcePath, this._module.someValue);
    this.callback(null, content + 'console.log("the value is", ' + this._module.someValue + ')');
  });
}

这是结果包:

/******/ (function(modules) { // webpackBootstrap
/******/    // The module cache
/******/    var installedModules = {};
/******/
/******/    // The require function
/******/    function __webpack_require__(moduleId) {
/******/
/******/        // Check if module is in cache
/******/        if(installedModules[moduleId]) {
/******/            return installedModules[moduleId].exports;
/******/        }
/******/        // Create a new module (and put it into the cache)
/******/        var module = installedModules[moduleId] = {
/******/            i: moduleId,
/******/            l: false,
/******/            exports: {}
/******/        };
/******/
/******/        // Execute the module function
/******/        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/        // Flag the module as loaded
/******/        module.l = true;
/******/
/******/        // Return the exports of the module
/******/        return module.exports;
/******/    }
/******/
/******/
/******/    // expose the modules object (__webpack_modules__)
/******/    __webpack_require__.m = modules;
/******/
/******/    // expose the module cache
/******/    __webpack_require__.c = installedModules;
/******/
/******/    // define getter function for harmony exports
/******/    __webpack_require__.d = function(exports, name, getter) {
/******/        if(!__webpack_require__.o(exports, name)) {
/******/            Object.defineProperty(exports, name, {
/******/                configurable: false,
/******/                enumerable: true,
/******/                get: getter
/******/            });
/******/        }
/******/    };
/******/
/******/    // getDefaultExport function for compatibility with non-harmony modules
/******/    __webpack_require__.n = function(module) {
/******/        var getter = module && module.__esModule ?
/******/            function getDefault() { return module['default']; } :
/******/            function getModuleExports() { return module; };
/******/        __webpack_require__.d(getter, 'a', getter);
/******/        return getter;
/******/    };
/******/
/******/    // Object.prototype.hasOwnProperty.call
/******/    __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/    // __webpack_public_path__
/******/    __webpack_require__.p = "";
/******/
/******/    // Load entry module and return exports
/******/    return __webpack_require__(__webpack_require__.s = 1);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
throw new Error("Cannot find module \"./c\"");

console.log('Hello from b');
console.log("the value is", 207)

/***/ }),
/* 1 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__b__ = __webpack_require__(0);

console.log('Hello from a');
console.log("the value is", 305)

/***/ })
/******/ ]);

请注意,如果找不到模块./c

,会出现错误

如果我将加载程序更改为标识符,则此工作完全正常:

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

它甚至失败了,只需调用this.loadModule并且对响应没有任何作用的普通装载程序。

module.exports = function (content) {
  const dep = content.split('\n')[0].split('\'')[1];

  if (dep[0] !== '.') {
    return content;
  }

  this.loadModule(dep, () => { console.log('loading', dep); });
  return content;
}

有人知道会发生什么吗?这是this.loadModule的预期行为吗?这是一个错误吗?如果它不是一个错误,我怎么能加载一个模块但仍然使它成为一部分?

1 个答案:

答案 0 :(得分:0)

好的经过一些研究后我在Webpack回购中发现了这个问题https://github.com/webpack/webpack/issues/4959,它讨论了同样的事情。问题是loadModule不加载模块'依赖性递归。理想情况下,这应该在webpack中的loadModule函数中得到支持,可能在loadModule中有一个选项,但是现在解决方法是从Webpack复制定义loadModule方法的代码({{3并且更改标志以递归加载模块为true,如https://github.com/webpack/webpack/blob/master/lib/dependencies/LoaderPlugin.js

基本上,改变这个:

compilation.addModuleDependencies(module, [
    [dep]
], true, "lm", false, (err) => {

对此:

compilation.addModuleDependencies(module, [
    [dep]
], true, "lm", true, (err) => {

我仍然不太相信这个解决方案是正确的,特别是因为我对Webpack的代码库不太熟悉,但它适用于我的用例,所以我'将其标记为已接受的答案,直到出现更好的解决方案。