如何在有角度的Web Worker内导入节点模块? [英] How to import a node module inside an angular web worker?

查看:47
本文介绍了如何在有角度的Web Worker内导入节点模块?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我尝试将节点模块导入 Angular 8网络工作者中,但是出现编译错误找不到模块".有人知道如何解决吗?

I try to import a node module inside an Angular 8 web worker, but get an compile error 'Cannot find module'. Anyone know how to solve this?

我使用 ng generate web-worker应用在电子项目中创建了一个新工作人员,如上述ng文档中所述.

I created a new worker inside my electron project with ng generate web-worker app, like described in the above mentioned ng documentation.

一切正常,直到我添加诸如 path fs-extra 之类的导入,例如:

All works fine until i add some import like path or fs-extra e.g.:

/// <reference lib="webworker" />    
import * as path from 'path';

addEventListener('message', ({ data }) => {    
  console.log(path.resolve('/'))
  const response = `worker response to ${data}`;
  postMessage(response);
});

此导入可以在任何其他ts组件中正常运行,但在Web Worker中,我会收到以下消息,例如错误:

This import works fine in any other ts component but inside the web worker i get a compile error with this message e.g.

Error: app/app.worker.ts:3:23 - error TS2307: Cannot find module 'path'.

我该如何解决?也许我在生成的 tsconfig.worker.json 中需要一些其他参数吗?

How can i fix this? Maybe i need some additional parameter in the generated tsconfig.worker.json?

重现错误,请运行:

$ git clone https://github.com/hoefling/stackoverflow-57774039
$ cd stackoverflow-57774039
$ yarn build

或在Travis上查看项目的构建日志.

Or check out the project's build log on Travis.

注意:

1)我只找到了

1) I only found this as a similar problem, but the answer handles only custom modules.

2)我用最小电子种子测试了相同的导入网络工作者,它可以正常工作,但是此示例使用的是无角的纯Java脚本.

2) I tested the same import with a minimal electron seed which uses web workers and it worked, but this example uses plain java script without angular.

推荐答案

1.TypeScript错误

您已经注意到,第一个错误是TypeScript错误.通过查看 tsconfig.worker.json ,我发现它会将 types 设置为一个空数组:

1. TypeScript error

As you've noticed the first error is a TypeScript error. Looking at the tsconfig.worker.json I've found that it sets types to an empty array:

{
  "compilerOptions": {
    "types": [],
    // ...
  }
  // ...
}

指定类型关闭自动包含 @types .在这种情况下,这是一个问题,因为 path @ types/node 中具有其类型定义.

Specifying types turns off the automatic inclusion of @types packages. Which is a problem in this case because path has its type definitions in @types/node.

因此,我们通过在 types 数组中显式添加 node 来解决此问题:

So let's fix that by explicitly adding node to the types array:

{
  "compilerOptions": {
    "types": [
        "node"
    ],
    // ...
  }
  // ...
}

这修复了TypeScript错误,但是尝试再次构建时,我们遇到了一个非常类似的错误.这次直接从Webpack.

This fixes the TypeScript error, however trying to build again we're greeted with a very similar error. This time from Webpack directly.

ERROR in ./src/app/app.worker.ts (./node_modules/worker-plugin/dist/loader.js!./src/app/app.worker.ts)
Module build failed (from ./node_modules/worker-plugin/dist/loader.js):
ModuleNotFoundError: Module not found: Error: Can't resolve 'path' in './src/app'

要弄清楚这一点,我们需要更深入地研究...

To figure this one out we need to dig quite a lot deeper...

首先,重要的是要理解为什么导入 path 可以在所有其他模块中进行.Webpack具有目标(网络,节点等)的概念.Webpack使用此目标来确定要使用的默认选项和插件.

First it's important to understand why importing path works in all the other modules. Webpack has the concept of targets (web, node, etc). Webpack uses this target to decide which default options and plugins to use.

通常,使用 @ angular-devkit/build-angular:browser 的Angular应用程序的目标将是 web .但是,根据您的情况, postinstall:electron 脚本实际上会修补 node_modules 进行更改:

Ordinarily the target of a Angular application using @angular-devkit/build-angular:browser would be web. However in your case, the postinstall:electron script actually patches node_modules to change that:

postinstall.js (为简便起见,省略了部分内容)

postinstall.js (parts omitted for brevity)

const f_angular = 'node_modules/@angular-devkit/build-angular/src/angular-cli-files/models/webpack-configs/browser.js';

fs.readFile(f_angular, 'utf8', function (err, data) {
  var result = data.replace(/target: "electron-renderer",/g, '');
  var result = result.replace(/target: "web",/g, '');
  var result = result.replace(/return \{/g, 'return {target: "electron-renderer",');

  fs.writeFile(f_angular, result, 'utf8');
});

目标 electron-renderer 被处理为Webpack与 node 类似.对我们来说特别有趣:它添加了 NodeTargetPlugin 默认情况下.

The target electron-renderer is treated by Webpack similarily to node. Especially interesting for us: It adds the NodeTargetPlugin by default.

该插件有什么作用,您想知道吗?它将所有已知的内置Node.js模块添加为外部.在构建应用程序时,Webpack不会尝试捆绑外部组件.而是在运行时使用 require 解析它们.这就是导入 path 的工作原理,即使它没有作为Webpack已知的模块安装.

What does that plugin do, you wonder? It adds all known built in Node.js modules as externals. When building the application, Webpack will not attempt to bundle externals. Instead they are resolved using require at runtime. This is what makes importing path work, even though it's not installed as a module known to Webpack.

使用 WorkerPlugin 单独编译worker.在他们的文档中指出:

默认情况下,捆绑工作代码时, WorkerPlugin 不会运行您配置的任何Webpack插件-这样可以避免两次运行 html-webpack-plugin 之类的事情.对于需要将插件应用于Worker代码的情况,请使用 plugins 选项.

By default, WorkerPlugin doesn't run any of your configured Webpack plugins when bundling worker code - this avoids running things like html-webpack-plugin twice. For cases where it's necessary to apply a plugin to Worker code, use the plugins option.

@ angular-devkit 中深入研究 WorkerPlugin 的用法,我们看到以下内容:

Looking at the usage of WorkerPlugin deep within @angular-devkit we see the following:

@ angular-devkit/src/angular-cli-files/models/webpack-configs/worker.js (简体)

new WorkerPlugin({
    globalObject: false,
    plugins: [
        getTypescriptWorkerPlugin(wco, workerTsConfigPath)
    ],
})

如我们所见,它使用 plugins 选项,但仅用于负责TypeScript编译的单个插件.这样,由Webpack配置的默认插件(包括 NodeTargetPlugin )就会丢失,并且不会用于工作程序.

As we can see it uses the plugins option, but only for a single plugin which is responsible for the TypeScript compilation. This way the default plugins, configured by Webpack, including NodeTargetPlugin get lost and are not used for the worker.

要解决此问题,我们必须修改Webpack配置.为此,我们将使用 @ angular-builders/custom-webpack .继续并安装该软件包.

To fix this we have to modify the Webpack config. And to do that we'll use @angular-builders/custom-webpack. Go ahead and install that package.

接下来,打开 angular.json 并更新 projects>角电子建筑师>构建:

"build": {
  "builder": "@angular-builders/custom-webpack:browser",
  "options": {
    "customWebpackConfig": {
      "path": "./extra-webpack.config.js"
    }
    // existing options
  }
}

serve 重复相同的操作.

现在,在与 angular.json 相同的目录中创建 extra-webpack.config.js :

Now, create extra-webpack.config.js in the same directory as angular.json:

const WorkerPlugin = require('worker-plugin');
const NodeTargetPlugin = require('webpack/lib/node/NodeTargetPlugin');

module.exports = (config, options) => {
  let workerPlugin = config.plugins.find(p => p instanceof WorkerPlugin);
  if (workerPlugin) {
    workerPlugin.options.plugins.push(new NodeTargetPlugin());
  }
  return config;
};

文件导出一个函数,该函数将由 @ angular-builders/custom-webpack 与现有的Webpack配置对象一起调用.然后,我们可以在所有 plugins 中搜索 WorkerPlugin 的实例,并通过添加 NodeTargetPlugin 修补其选项.

The file exports a function which will be called by @angular-builders/custom-webpack with the existing Webpack config object. We can then search all plugins for an instance of the WorkerPlugin and patch its options adding the NodeTargetPlugin.

这篇关于如何在有角度的Web Worker内导入节点模块?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆