如何在 angular 网络工作者中导入节点模块?
How to import a node module inside an angular web worker?
我尝试在 Angular 8 web worker 中导入节点模块,但出现编译错误 'Cannot find module'。有人知道怎么解决吗?
我用 ng generate web-worker app
在我的 electron 项目中创建了一个新的 worker,就像上面提到的 ng 文档中描述的那样。
一切正常,直到我添加一些导入,如 path
或 fs-extra
例如:
/// <reference lib="webworker" />
import * as path from 'path';
addEventListener('message', ({ data }) => {
console.log(path.resolve('/'))
const response = `worker response to ${data}`;
postMessage(response);
});
此导入在任何其他 ts 组件中工作正常,但在网络工作者中我得到一个编译 error 和此消息,例如
Error: app/app.worker.ts:3:23 - error TS2307: Cannot find module 'path'.
我该如何解决这个问题?也许我在生成的 tsconfig.worker.json
?
中需要一些额外的参数
要重现错误,运行:
$ git clone https://github.com/hoefling/Whosebug-57774039
$ cd Whosebug-57774039
$ yarn build
或在 Travis 上查看项目的 build log。
注:
1) 我只发现 是一个类似的问题,但答案只处理自定义模块。
2) 我用 minimal electron seed 测试了相同的导入,它使用 web worker 并且它有效,但是这个例子使用没有 angular.
的普通 java 脚本
1。打字稿错误
如您所见,第一个错误是 TypeScript 错误。查看 tsconfig.worker.json
我发现它将 types
设置为一个空数组:
{
"compilerOptions": {
"types": [],
// ...
}
// ...
}
指定 types
将关闭 automatic inclusion of @types
packages。在这种情况下这是一个问题,因为 path
在 @types/node
.
中有其类型定义
所以让我们通过显式添加 node
到 types
数组来解决这个问题:
{
"compilerOptions": {
"types": [
"node"
],
// ...
}
// ...
}
这修复了 TypeScript 错误,但是尝试再次构建时我们遇到了非常相似的错误。这次直接来自 Webpack。
2。 Webpack 错误
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'
为了弄清楚这一点,我们需要深入挖掘...
为什么它在其他任何地方都有效
首先,重要的是要了解为什么导入 path
在所有其他模块中都有效。 Webpack 有 targets 的概念(web、node 等)。 Webpack 使用此目标来决定使用哪些默认选项和插件。
通常使用 @angular-devkit/build-angular:browser
的 Angular 应用程序的目标是 web
。但是,在您的情况下,postinstall:electron
脚本实际上修补了 node_modules
以更改它:
postinstall.js
(省略部分)
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
is treated by Webpack similarily to node
. Especially interesting for us: It adds the NodeTargetPlugin
。
您想知道该插件的作用是什么?它将所有已知的内置 Node.js 模块添加为 externals。在构建应用程序时,Webpack 不会尝试捆绑外部组件。相反,它们在运行时使用 require
解析。这就是导入 path
起作用的原因,即使它没有作为 Webpack 已知的模块安装。
为什么它对工人不起作用
worker 是使用 WorkerPlugin
. In their documentation 他们声明的单独编译的:
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
的用法,我们看到以下内容:
@angular-devkit/src/angular-cli-files/models/webpack-configs/worker.js
(简体)
new WorkerPlugin({
globalObject: false,
plugins: [
getTypescriptWorkerPlugin(wco, workerTsConfigPath)
],
})
正如我们所见,它使用了 plugins
选项,但仅针对负责 TypeScript 编译的单个插件。这样,由 Webpack 配置的默认插件,包括 NodeTargetPlugin
就会丢失,不会被 worker 使用。
解决方案
要解决这个问题,我们必须修改 Webpack 配置。为此,我们将使用 @angular-builders/custom-webpack
。继续安装那个包。
下一步,打开angular.json
并更新projects > angular-electron > architect > build
:
"build": {
"builder": "@angular-builders/custom-webpack:browser",
"options": {
"customWebpackConfig": {
"path": "./extra-webpack.config.js"
}
// existing options
}
}
对 serve
重复相同的操作。
现在,在与 angular.json
相同的目录中创建 extra-webpack.config.js
:
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
.
我尝试在 Angular 8 web worker 中导入节点模块,但出现编译错误 'Cannot find module'。有人知道怎么解决吗?
我用 ng generate web-worker app
在我的 electron 项目中创建了一个新的 worker,就像上面提到的 ng 文档中描述的那样。
一切正常,直到我添加一些导入,如 path
或 fs-extra
例如:
/// <reference lib="webworker" />
import * as path from 'path';
addEventListener('message', ({ data }) => {
console.log(path.resolve('/'))
const response = `worker response to ${data}`;
postMessage(response);
});
此导入在任何其他 ts 组件中工作正常,但在网络工作者中我得到一个编译 error 和此消息,例如
Error: app/app.worker.ts:3:23 - error TS2307: Cannot find module 'path'.
我该如何解决这个问题?也许我在生成的 tsconfig.worker.json
?
要重现错误,运行:
$ git clone https://github.com/hoefling/Whosebug-57774039
$ cd Whosebug-57774039
$ yarn build
或在 Travis 上查看项目的 build log。
注:
1) 我只发现
2) 我用 minimal electron seed 测试了相同的导入,它使用 web worker 并且它有效,但是这个例子使用没有 angular.
的普通 java 脚本1。打字稿错误
如您所见,第一个错误是 TypeScript 错误。查看 tsconfig.worker.json
我发现它将 types
设置为一个空数组:
{
"compilerOptions": {
"types": [],
// ...
}
// ...
}
指定 types
将关闭 automatic inclusion of @types
packages。在这种情况下这是一个问题,因为 path
在 @types/node
.
所以让我们通过显式添加 node
到 types
数组来解决这个问题:
{
"compilerOptions": {
"types": [
"node"
],
// ...
}
// ...
}
这修复了 TypeScript 错误,但是尝试再次构建时我们遇到了非常相似的错误。这次直接来自 Webpack。
2。 Webpack 错误
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'
为了弄清楚这一点,我们需要深入挖掘...
为什么它在其他任何地方都有效
首先,重要的是要了解为什么导入 path
在所有其他模块中都有效。 Webpack 有 targets 的概念(web、node 等)。 Webpack 使用此目标来决定使用哪些默认选项和插件。
通常使用 @angular-devkit/build-angular:browser
的 Angular 应用程序的目标是 web
。但是,在您的情况下,postinstall:electron
脚本实际上修补了 node_modules
以更改它:
postinstall.js
(省略部分)
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
is treated by Webpack similarily to node
. Especially interesting for us: It adds the NodeTargetPlugin
。
您想知道该插件的作用是什么?它将所有已知的内置 Node.js 模块添加为 externals。在构建应用程序时,Webpack 不会尝试捆绑外部组件。相反,它们在运行时使用 require
解析。这就是导入 path
起作用的原因,即使它没有作为 Webpack 已知的模块安装。
为什么它对工人不起作用
worker 是使用 WorkerPlugin
. In their documentation 他们声明的单独编译的:
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
的用法,我们看到以下内容:
@angular-devkit/src/angular-cli-files/models/webpack-configs/worker.js
(简体)
new WorkerPlugin({
globalObject: false,
plugins: [
getTypescriptWorkerPlugin(wco, workerTsConfigPath)
],
})
正如我们所见,它使用了 plugins
选项,但仅针对负责 TypeScript 编译的单个插件。这样,由 Webpack 配置的默认插件,包括 NodeTargetPlugin
就会丢失,不会被 worker 使用。
解决方案
要解决这个问题,我们必须修改 Webpack 配置。为此,我们将使用 @angular-builders/custom-webpack
。继续安装那个包。
下一步,打开angular.json
并更新projects > angular-electron > architect > build
:
"build": {
"builder": "@angular-builders/custom-webpack:browser",
"options": {
"customWebpackConfig": {
"path": "./extra-webpack.config.js"
}
// existing options
}
}
对 serve
重复相同的操作。
现在,在与 angular.json
相同的目录中创建 extra-webpack.config.js
:
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
.