Bazel + Angular + SocketIO引起的问题:Uncaught TypeError: XMLHttpRequest不是一个构造函数。

13

我想将ngx-socket-io添加到我的Angular应用程序中。 我使用Bazel运行我的Angular dev-server。 不幸的是,ngx-socket-io似乎不能与ts_devserver轻松集成。 我在浏览器控制台中看到以下错误:

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)
似乎是由xmlhttprequest-ssl引起的,它是engine.io-client的一个依赖项,并且ngx-socket-io需要它。但是这个问题只出现在ts_devserver中。在生产环境下运行Angular应用程序完全没有问题。

最小复现步骤

您可以轻松尝试自己: https://github.com/flolu/bazel-socketio-issue

只需运行yarn install,然后运行yarn dev(它会在浏览器控制台@ http://localhost:4200)中导致错误。请注意,yarn prod @ http://localhost:8080运行完全正常!

编辑 1

目前似乎在Windows上还有另一个问题。因此,您只能在Mac或Linux上尝试示例存储库。


该死。这可能与Windows有关(我使用Linux,不会出现这个错误)。 - Florian Ludewig
你能提供一个可以在Windows上运行的代码库吗? - Aakash Garg
@AakashGarg 我的设备上都没有使用Windows系统,所以我无法调试这个问题。 - Florian Ludewig
在Windows上运行并在console.log中准确地查看错误。但我不知道如何修复它 :( - trungk18
我使用verbose标志运行,但没有看到太多信息。顺便说一下,我在Windows 10上运行它。https://1drv.ms/u/s!AqRb3sHXSFx6jbZ5DhruachcakTuVA?e=Ac8ybW - trungk18
显示剩余5条评论
1个回答

5
问题来自于engine.io-client,它是socket.io-client内部使用的:
socket.io-client作为UMD模块构建时触发。
     "@npm//socket.io-client:socket.io-client__umd",

BUILD.bazel 文件中,engine.io-client/package.jsonbrowser 键:
 "browser": {
   "ws": false,
   "xmlhttprequest-ssl": "./lib/xmlhttprequest.js"
 },

似乎被忽略。

因此,在UMD构建中,node_modules/engine.io-client/lib/transports/*.js 中的 require('xmlhttprequest-ssl') 语句仍然存在。由于 xmlhttprequest-ssl 用于无头Node环境并且在浏览器中不起作用,因此会导致错误。

我找不到这种行为的原因/问题,但我找到了一个解决方案(不应视为解决方法):

使用 postinstall 脚本重写 engine.io-client

  1. 安装 shelljs 包: yarn add -D shelljs
  2. package.json 中的 postinstall 更新为:"postinstall": "node --preserve-symlinks --preserve-symlinks-main ./postinstall-patches.js && ngcc"
  3. 将以下代码放入项目根目录下的 postinstall-patches.js 中:
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 =====');

(灵感来源: https://bazelbuild.github.io/rules_nodejs/#patching-the-npm-packages,其中链接到示例https://github.com/angular/angular/blob/master/tools/postinstall-patches.js)

  1. yarn install(安装依赖)
  2. yarn dev(运行项目)

我将在几分钟内向您的GitHub存储库提交拉取请求。


可能的替代方案,但无法让它们工作:

  • 使用socket.io-client/dist/socket.io.js,但需要额外的“UMD shim”,因为它似乎是一个“匿名UMD”模块,或者
  • 一些npm_umd_bundle魔法

有关这两种方法的更多信息,请参见每个新的npm依赖项都需要独特的方法来将其添加到ts_devserver #1055中bazelbuild/rules_nodejs问题。


PR已经提交。 - Sebastian B.
哇!我刚试了一下,它完美地工作了!我还有两个问题:(1)你说“这不应该被视为解决方法”。你的意思是这不应该被使用,还是可以使用?(2)engine.io-client 的修补也可以使用类似 patch-package 的 .patch 文件来完成吗? - Florian Ludewig
1
首先,我昨天才开始接触Bazel :-) 至于(1):我认为打补丁是非常好的,可以参考https://bazelbuild.github.io/rules_nodejs/中的“Patching the packages”。我还发现许多人需要解决NPM上发布的预打包构建的问题。因此,在Bazel生态系统方面,这是必需的,所以有像这样的工具和技巧。(2)是的,应该可以。另一种选择是在链接页面上查看“Patching the built-in release”。postinstall-patches.js可以为您提供更多灵活性,并且对未来的升级更加稳定(例如,我们可以在没有硬编码文件名的情况下进行打补丁)。 - Sebastian B.
非常感谢!我会等到明天再发放赏金 :) 你是如何“检测”这个问题的?我知道它与xmlhttprequest有关,但是你是如何将其缩小到engine.io-client的范围的? - Florian Ludewig
1
我知道xmlhttprequest-ssl不适用于浏览器,所以我想知道为什么在运行开发服务器时仍然需要它。最后,通过一个小的grep(具体来说是ripgrep:rg -ul xmlhttprequest-ssl),我发现了在socket.io-client(但不在其源代码中,只在sourcemaps中)和engine.io-client中引用了xmlhttprequest-ssl - Sebastian B.
既然您已经成功解决了这个问题,如果您能看一下这个类似的问题就太好了:https://stackoverflow.com/questions/64209912 - Florian Ludewig

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接