Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bazel + Angular + SocketIO Causes: Uncaught TypeError: XMLHttpRequest is not a constructor

I want to add ngx-socket-io to my Angular application. I use Bazel to run my Angular dev-server. Unfortunately ngx-socket-io doesn't seem to work with the ts_devserver out of the box. I get this error in the browser console:

Uncaught TypeError: XMLHttpRequest is not a constructor
    at ts_scripts.js?v=1587802098203:16776
    at Object.23.../transport (ts_scripts.js?v=1587802098203:16780)
    at o (ts_scripts.js?v=1587802098203:11783)

It seems to be caused by xmlhttprequest-ssl which is a dependency of engine.io-client and it is needed by ngx-socket-io. But this problem only occurs with the ts_devserver. Running the Angular app in production works totally fine.

Minimal Reproduction

You can easily try it yourself: https://github.com/flolu/bazel-socketio-issue

Just run yarn install and then yarn dev (it causes the error in the browser console @ http://localhost:4200). And note that yarn prod @ http://localhost:8080 works just fine!

Edit 1

Currently there seems to be another issue on Windows. So you can only try the example repo if you're running Mac or Linux

like image 436
Flo Avatar asked Apr 25 '20 08:04

Flo


1 Answers

The problem comes from engine.io-client, which is used internally by socket.io-client:

When socket.io-client gets built as UMD module triggered by

     "@npm//socket.io-client:socket.io-client__umd",

in BUILD.bazel, the browser key of engine.io-client/package.json:

 "browser": {
   "ws": false,
   "xmlhttprequest-ssl": "./lib/xmlhttprequest.js"
 },

is seemingly ignored.

As a consequence, the require('xmlhttprequest-ssl') statements in node_modules/engine.io-client/lib/transports/*.js remain in the UMD build. Because xmlhttprequest-ssl is intended for headless Node environments and does not work in browsers, this results in an error.

I couldn't find a reason/issue for this behavior, but I found a solution (which shouldn't be considered as a workaround):

Rewrite engine.io-client with a postinstall script:

  1. install shelljs package: yarn add -D shelljs
  2. update postinstall in package.json to: "postinstall": "node --preserve-symlinks --preserve-symlinks-main ./postinstall-patches.js && ngcc"
  3. put the following code into postinstall-patches.js at project root:
try {
  require.resolve('shelljs');
} catch (e) {
  // We are in an bazel managed external node_modules repository
  // and the resolve has failed because node did not preserve the symlink
  // when loading the script.
  // This can be fixed using the --preserve-symlinks-main flag which
  // is introduced in node 10.2.0
  console.warn(
      `Running postinstall-patches.js script in an external repository requires --preserve-symlinks-main node flag introduced in node 10.2.0. ` +
      `Current node version is ${process.version}. Node called with '${process.argv.join(' ')}'.`);
  process.exit(0);
}

const {set, cd, sed, ls} = require('shelljs');
const path = require('path');
const log = console.info;

log('===== about to run the postinstall-patches.js script     =====');
// fail on first error
set('-e');
// print commands as being executed
set('-v');

cd(__dirname);

log('\n# patch engine.io-client: rewriting \'xmlhttprequest-ssl\' to browser shim');
ls('node_modules/engine.io-client/lib/transports/*.js').forEach(function (file) {
  sed('-i', '\'xmlhttprequest-ssl\'', '\'../xmlhttprequest\'', file);
});

log('===== finished running the postinstall-patches.js script =====');

(Inspiration: https://bazelbuild.github.io/rules_nodejs/#patching-the-npm-packages, which links to the example https://github.com/angular/angular/blob/master/tools/postinstall-patches.js)

  1. yarn install
  2. yarn dev

I'll submit a pull request to your GitHub repro in a few minutes.


Possible alternatives, couldn't get them to work:

  • use socket.io-client/dist/socket.io.js but with an additional "UMD shim" because it seems to be an "anonymous UMD" module, OR
  • some npm_umd_bundle magic

See issue Every new npm dep needs a unique approach how to add it to ts_devserver #1055 in bazelbuild/rules_nodejs for more information about both ways.

like image 195
Sebastian B. Avatar answered Oct 21 '22 04:10

Sebastian B.