如何在 Angular Web Worker 中导入节点模块? [英] How to import a node module inside an 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.
一切正常,直到我添加一些导入,如 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) 我只找到了 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 likehtml-webpack-plugin twice
. For cases where it's necessary to apply a plugin to Worker code, use theplugins
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屋!