Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

webpack.NamedModulesPlugin() has been deprecated #134

Open
shreya-shankar03 opened this issue Jun 28, 2022 · 18 comments
Open

webpack.NamedModulesPlugin() has been deprecated #134

shreya-shankar03 opened this issue Jun 28, 2022 · 18 comments
Assignees

Comments

@shreya-shankar03
Copy link

shreya-shankar03 commented Jun 28, 2022

To use rewiremock, according to the docs we need to use the NamedModulesPlugin().
But this plugin has been deprecated, and if we use it we get a compiler error that says "NamedModulesPlugin is not a constructor"

Can this be updated? Also can rewiremock be upgraded to make it compaible with webpack 5?

I have "rewiremock": "^3.11.1" in my package.json

@theKashey theKashey self-assigned this Jun 30, 2022
@theKashey
Copy link
Owner

NamedModulesPlugin is a part of configuration now - https://webpack.js.org/configuration/optimization/#optimizationmoduleids - you need named

Tell me how it goes. But should be fine 🤞

@shreya-shankar03
Copy link
Author

I still get errors that look like this:
SyntaxError: Unexpected token 'if' which is caused when rewiremock injects code into one of the config files.

@shreya-shankar03
Copy link
Author

shreya-shankar03 commented Aug 11, 2022

@theKashey do you have any update for this? I see the issue that looks similar to the issue I am facing, but is there any solution yet?

Thank you!

@benjamincburns
Copy link

benjamincburns commented Jan 10, 2023

Also having the same issue. Pinging @theKashey in case there's something else to be done.

I wonder if it has something to do with having optimization.runtimeChunk set to "single"?

Edit: nah, I reverted that change and it's still a problem.

@theKashey
Copy link
Owner

Any example replicating the problem, the one I can fix, would be very welcomed. If only you can prune your project to the state you can share it with me (including in private) 🙏

@benjamincburns
Copy link

benjamincburns commented Jan 11, 2023

I decided to move on and try something else, but you could probably recreate it from my setup pretty easily. Just edit this webpack config (edited to permalink so this doesn't break when I push next time - branch name is pack-the-planet, however) to include the rewiremock plugin, and set optimization.moduleId and optimization.chunkId to "named", then run a build from the root of the workspace via the yarn command (literally just run yarn). Finally navigate back to packages/box and run yarn test and it should throw the error.

In the meantime I worked around needing rewiremock altogether by setting optimization.runtimeChunk to "single", enabling named module and chunk IDs, and writing a super simple helper utility that lets me require bundled modules from my tests via the exported __webpack_require__ from dist/runtime.js.

@benjamincburns
Copy link

Also it may be that I left the HMR plugin out of my config, as I was trying to see whether I could run my tests against a bundle that I felt comfortable shipping. I think running tests against a dev build is helpful when diagnosing issues, but I need to be able to test the exact bundle that I actually ship.

@theKashey
Copy link
Owner

HMR is what brings more or less "real module system" rewiremock can work with. Without it nothing could happen, but 🤔 initialization code should throw an error is something is not right

@benjamincburns
Copy link

What's HMR bringing to Webpack 5's module system that the following config doesn't already have? The below config needs a little bit of module ID transformation, but so far path.relative(packageRoot, require.resolve(idiomaticModuleName)) has pretty much taken care of it for me.

optimization: {
  chunkId: "named",
  moduleId: "named",
  runtimeChunk: "single"
}

@theKashey
Copy link
Owner

HMR makes module flexible not hard-wired, so one can tap in and change what is required.

@benjamincburns
Copy link

By that do you mean that it allows you to manipulate __webpack_module_cache__? From what I can tell that's the only thing that's different here. It seems that even the module factories (i.e. __webpack_modules__) are available via require("./dist/runtime").m.

@fringd
Copy link

fringd commented Feb 3, 2023

I still get errors that look like this:
SyntaxError: Unexpected token 'if' which is caused when rewiremock injects code into one of the config files.

me too

@insight001
Copy link

Has anyone been able to get this working with Webpack 5?

I re-wrote the plugin to fix the SyntaxError: Unexpected token 'if' error but still could not get it to mock

@theKashey is there anything we can do to get this working?

@theKashey
Copy link
Owner

😭 "Continue investigations".

A small example I can play with and try to get fixed would be of great help.

@insight001
Copy link

Thanks for getting on this.
https://github.com/jburrow/rewiremock-webpack-example
the main branch is a simple app that shows the broken webpack 5, and the webpack-4 branch contains the working version

Let me know how I can help.

@r3m0t
Copy link

r3m0t commented Aug 1, 2024

Here's how far I got

Reduced irrelevant transforms from babel

.babelrc

{
    "presets": [
        "@babel/typescript",
        [
            "@babel/env",
            {
                "modules": false,
                "exclude": [
                    "@babel/plugin-transform-typeof-symbol",
                    "@babel/plugin-transform-regenerator",
                    "@babel/plugin-transform-async-to-generator"
                ]
            }
        ]
    ],
    "plugins": [
        "@babel/plugin-syntax-dynamic-import",
        "rewiremock/babel"
    ]
}

Added logging to node_modules/rewiremock/webpack.js

global['_REWIREMOCK_HOISTED_'] = global['_REWIREMOCK_HOISTED_'] || [];
console.log("unshifting webpack");
global['_REWIREMOCK_HOISTED_'].unshift( function (rewiremock, api) {
  api.overrideEntryPoint(module);
  rewiremock.addPlugin(api.plugins.nodejs);
});

Added logging to node_modules/rewiremock/lib/index.js

if (global['_REWIREMOCK_HOISTED_']) {
  console.log("calling out", global['_REWIREMOCK_HOISTED_'].length);
  global['_REWIREMOCK_HOISTED_'].forEach(function (cb) {
    cb(API.mockModule, { plugins: _index2.default, overrideEntryPoint: overrideEntryPoint });
  });
  console.log("finished calling out", global['_REWIREMOCK_HOISTED_'].length);
  global['_REWIREMOCK_HOISTED_'] = [];
}

Added similar logging to node_modules/rewiremock/es/index.js

Chrome Devtools- check "Show ignore-listed frames"

Built and examined correct output from webpack 4

__webpack_require__.r(__webpack_exports__);
/* WEBPACK VAR INJECTION */(function(global, module) {/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "greet", function() { return greet; });
/** rejig MATCH */ (function rwrmck() {
  global["_REWIREMOCK_HOISTED_"] = global["_REWIREMOCK_HOISTED_"] || [];
  global["_REWIREMOCK_HOISTED_"].push(function (rewiremock) {
    rewiremock("./src/calculator.ts").with({
      add: (x, y) => {
        console.log('[mocked]');
        return 2;
      }
    });
    rewiremock.enable();
  });
})('rwrmck');/** rejig AFTER MATCH */ /* harmony import */ var _initRewiremock__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./initRewiremock */ "./src/initRewiremock.ts");
/* harmony import */ var _calculator__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./calculator */ "./src/calculator.ts");


console.log('[index.ts] prior');
console.log('[index.ts] [module] mock-initialized', module.i, module, 'require');

_initRewiremock__WEBPACK_IMPORTED_MODULE_0__["rewiremock"].disable();
global["_REWIREMOCK_HOISTED_"] = [];
var greet = async function greet() {
  console.log('[index.ts] [greet] invoked');
  var e = document.createElement('h1');
  e.innerText = "".concat(Object(_calculator__WEBPACK_IMPORTED_MODULE_1__["add"])(5, 5));
  document.body.appendChild(e);
  return null;
};
greet();
/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../node_modules/webpack/buildin/global.js */ "./node_modules/webpack/buildin/global.js"), __webpack_require__(/*! ./../node_modules/webpack/buildin/harmony-module.js */ "./node_modules/webpack/buildin/harmony-module.js")(module)))

//# sourceURL=webpack:///./src/index.ts?

Incorrect output from Webpack 5 (the Harmony import of initRewiremock.ts is pulled to too early)

__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   greet: () => (/* binding */ greet)
/* harmony export */ });
/* harmony import */ var _initRewiremock__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./initRewiremock */ "./src/initRewiremock.ts");
/* module decorator */ module = __webpack_require__.hmd(module);
(function rwrmck() {
  __webpack_require__.g["_REWIREMOCK_HOISTED_"] = __webpack_require__.g["_REWIREMOCK_HOISTED_"] || [];
  __webpack_require__.g["_REWIREMOCK_HOISTED_"].push(function (rewiremock) {
    rewiremock("./src/calc-dep.ts").with({
      extra: (x, y) => {
        return 34;
      }
    });
    rewiremock.enable();
  });
})('rwrmck');

_initRewiremock__WEBPACK_IMPORTED_MODULE_0__.rewiremock.disable();
__webpack_require__.g["_REWIREMOCK_HOISTED_"] = [];
console.log('[index.ts] [module]', module, 'require', '6');
var greet = async function greet() {
  var calculator = await __webpack_require__.e(/*! import() */ "src_calculator_ts").then(__webpack_require__.bind(__webpack_require__, /*! ./calculator */ "./src/calculator.ts"));
  console.log('[index.ts] [greet] invoked');
  var e = document.createElement('h1');
  e.innerText = "".concat(calculator.add(5, 5));
  document.body.appendChild(e);
  return null;
};
greet();

//# sourceURL=webpack://explore-webpack-mocking/./src/index.ts?

I think, the __webpack_require__.g["_REWIREMOCK_HOISTED_"].push() call needs to become a Webpack InitFragment to make sure it's emitted before the import of initRewiremock.ts. which also means a different Webpack hook needs to be used.

As well, runtimeRequirements needs to be used so that the argument is called module and not __unused_webpack_module. There is info on this here- webpack/webpack#11974 I used this injectString as a hacky workaround.

/***/if (typeof __webpack_require__ !== 'undefined') {
   try {
     var rewiremockInterceptor = __webpack_require__('${file}');
     if (rewiremockInterceptor && rewiremockInterceptor.default) {
       __webpack_require__ = rewiremockInterceptor.default(__webpack_require__, typeof module === "undefined" ? __unused_webpack_module : module);
     }
   } catch (e) {debugger;}

@r3m0t
Copy link

r3m0t commented Aug 1, 2024

Here's the node_modules/rewiremock/webpack/plugin.js for Webpack 5 but it needs a rewrite. The filter on module_.resource is for debugging and can be removed in the final version. Thanks @insight001 for this

const {relative} = require("path");
const {ConcatSource} = require("webpack-sources");

const normalizePath = a => a[0] === '.' ? a : './' + a;

const file = normalizePath(relative(process.cwd(), __dirname + '/src/interceptor.js').replace(/\\/g, '/'));

const injectString = `/***/if(typeof __webpack_require__!=='undefined') {
   try {
     var rewiremockInterceptor = __webpack_require__('${file}');

     if (rewiremockInterceptor && rewiremockInterceptor.default) {
       __webpack_require__ = rewiremockInterceptor.default(__webpack_require__, module);
     }
   } catch (e) {}
}
`;

class RewiremockPlugin {
  apply(compiler) {
    compiler.hooks.compilation.tap('RewiremockPlugin', function (compilation) {

      compilation.moduleTemplates.javascript.hooks.render.tap('RewiremockPlugin', function (moduleSource, module_) {
        if (!module_.resource || module_.resource.includes("node_modules")) { return moduleSource; }
        const source = new ConcatSource();
        const src = moduleSource.source();
        // and injection
        if (src.indexOf('require') > 0) {
          source.add(injectString);
        }
        // re-hoists mocks
        const firstImport = src.indexOf('/* harmony import');
        if (src.indexOf('rwrmck') > 0 && firstImport > 0) {
          const match = src.match(/\(function rwrmck\(([\s\S]*)rwrmck'\);/g);
          if (match && match.length) {
            moduleSource = [
              /** merely 
               eval("__webpack_require__.r(__webpack_exports__);\\n/* WEBPACK VAR INJECTION (function(global, module)
                {/* harmony export (binding)  __webpack_require__.d(__webpack_exports__, \\"greet\\", function() 
                { return greet; });\\n'
               */
              '/** rejig */ ',
              src.substr(0, firstImport), 
              /** function rwrmck() {\\n  global[\\"_REWIREMOCK_HOISTED_\\"] = global[\\"_REWIREMOCK_HOISTED_\\"] || [];\\n  global[\\"_REWIREMOCK_HOISTED_\\"].push(function (rewiremock) {\\n    rewiremock(\\"./src/calculator.ts\\").with({\\n      add: (x, y) => {\\n        console.log('[mocked]');\\n        return 2;\\n      }\\n    });\\n    rewiremock.enable();\\n  });\\n})('rwrmck');  */
              '/** rejig MATCH */ ',
              match[0],
              '/** rejig AFTER MATCH */ ',
              src.substr(firstImport).replace(match[0], ''), // contains initRewriteMock import
              '/** end rejig */ ',
            ].join('');
          }
          source.add(moduleSource);
        } else {
          source.add(moduleSource);
        }
        return source;
      });
    });
  }
}

module.exports = RewiremockPlugin;

@r3m0t
Copy link

r3m0t commented Aug 5, 2024

Right we might have a fix

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants