如何在 Angular Web Worker 中导入节点模块? [英] How to import a node module inside an angular web worker?

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

问题描述

我尝试在 Angular 8 web worker 中导入节点模块,但是得到一个编译错误找不到模块".有人知道如何解决这个问题吗?

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 app 在我的电子项目中创建了一个新的 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.

一切正常,直到我添加一些导入,如 pathfs-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) 我只找到了 this 作为一个类似的问题,但答案仅处理自定义模块.

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 错误.查看 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 会关闭 自动包含@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.

所以让我们通过将 node 显式添加到 types 数组来解决这个问题:

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 具有 targets(网络、节点等)的概念.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 模块添加为 externals.在构建应用程序时,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.

worker 使用 WorkerPlugin 单独编译.在他们的文档中,他们声明:

The worker is compiled separately using the WorkerPlugin. In their documentation they state:

默认情况下,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 >角电子>建筑师 >构建:

Next, open angular.json and update projects > angular-electron > architect > build:

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

serve 重复同样的操作.

Repeat the same for 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.

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

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