"TypeError: Cannot read property 'indexOf' of undefined" raised when using packages "onoff" or "rpi-gpio" with WebPack - node.js

I wrote a Node.JS project for the Raspberry PI, to control the GPIO.
This is my first time using GPIO.
The project uses the "onoff" package to communicate with GPIO. And the compiler is WebPack.
I can compile the project without issue.
But when I run the application on the RaspberryPI, I receive this error:
webpack:///./node_modules/bindings/bindings.js?:178
if (fileName.indexOf(fileSchema) === 0) {
^
TypeError: Cannot read property 'indexOf' of undefined
at Function.getFileName (webpack:///./node_modules/bindings/bindings.js?:178:16)
at bindings (webpack:///./node_modules/bindings/bindings.js?:82:48)
at eval (webpack:///./node_modules/epoll/epoll.js?:7:86)
at eval (webpack:///./node_modules/epoll/epoll.js?:15:3)
at Object../node_modules/epoll/epoll.js (/home/pi/xilium/raspi.node/Raspi.node/dist/raspi.multi-monitor.js:809:1)
at __webpack_require__ (/home/pi/xilium/raspi.node/Raspi.node/dist/raspi.multi-monitor.js:20:30)
at eval (webpack:///./node_modules/rpi-gpio/rpi-gpio.js?:6:20)
at Object../node_modules/rpi-gpio/rpi-gpio.js (/home/pi/xilium/raspi.node/Raspi.node/dist/raspi.multi-monitor.js:1375:1)
at __webpack_require__ (/home/pi/xilium/raspi.node/Raspi.node/dist/raspi.multi-monitor.js:20:30)
at eval (webpack:///./src/raspi.multi-monitor.ts?:29:15)
So, I tried replacing the "onoff" package with "rpi-gpio". Unfortunately, the result is the same.
It seems that there is a configuration issue for "epoll" package (a dependence of "onoff" and "rpi-gpio").
Can anyone help me?

As a disclaimer, I am new to electron, webpack and everything around it, but after a lot of searching, I finally managed to get it working. I am not sure if this is the proper way to do it yet, but I just got it to work.
While searching far and wide, I found this comment on an issue from the serialport package, where they use electron-rebuild to rebuild the serialport module. More info about using native node modules can be found in the Electron documentation here.
Basically, I this to the scripts of my package.json:
"rebuild": "electron-rebuild -f -w onoff"
Then I ran npm run rebuild. Unfortunately, it still didn't work.
What was the missing link, was to tell webpack that the onoff module should be external.
I did it like so, in the webpack config that builds the electron parts of my app (setup is based on this guide I read):
'use strict';
const path = require('path');
const webpack = require('webpack');
module.exports = {
mode: 'development',
entry: './src/electron/main.js',
output: {
filename: 'index.js',
path: path.resolve(__dirname, 'out/electron')
},
module: {
rules: []
},
resolve: {
extensions: ['.js']
},
plugins: [
// This is the important part for onoff to work
new webpack.ExternalsPlugin('commonjs', [
'onoff'
])
],
// tell webpack that we're building for electron
target: 'electron-main',
node: {
// tell webpack that we actually want a working __dirname value
// (ref: https://webpack.js.org/configuration/node/#node-__dirname)
__dirname: false
}
};
As I wrote this, I stumbled upon externals config that might just work the same as well.
Now, finally I can blink my LEDs. I hope this answer can help anyone else in the future that might have the same issue.

Related

eslint complains about __dirname not being defined in a NodeJS file

I've just started using eslint. I initialized it and started fixing problems it pointed out.
One problem, however, it shouldn't complain about:
const data = fs.readFileSync(path.join(__dirname, _FILENAME));
The error is:
error '__dirname' is not defined
I did some searching and found that when you add --experimental to the node command that __dirname is not defined. This, however, isn't the case for me. I'm not running node with the --experimental flag.
See these questions:
Alternative for __dirname in Node.js when using ES6 modules
__dirname is not defined in nodejs
This is happening because ESLint does not know that your code is supposed to be used in Node.js: __dirname is not defined in browsers and also not defined in ES modules. To tell ESLint that your code will run in Node.js as a CommonJS module, open your ESLint config and set node: true in the env section. If you are using .eslintrc.json:
{
"env": {
"node": true
}
}
There are also other ways to specify environments, they are explained in the related documentation.
I ran into the same. Adding this to .eslintrc.json fixed it for me:
"globals": {
"__dirname": true
}
Got the idea from this similar question about the process global: https://stackoverflow.com/a/56777068/936907

How to bundle node module CSS into a vscode extension

My Visual Studio Code extension uses the node module highlight.js which comes with a folder full of CSS files. These provide colour schemes for syntax colouring. It has become necessary to bundle some of the CSS files.
It's about bundling an asset
The objective is to bundle a CSS file and at run-time access the file content as a string. If that can be achieved without an import statement that would be perfect. Normally, how exactly one accesses the content of the bundled file would be a separate question, but I have a feeling that content retrieval and how one should go about bundling the asset are closely entwined.
I freely admit to having a weak understanding of WebPack.
The story so far
The bundler is specified in package.json as "webpack": "^5.4.0" but I don't know how to ascertain what is actually present. It is conceivable that there is something wrong with my setup: when I try to run webpack --version on a command prompt in the project folder, it responds
CLI for webpack must be installed.
webpack-cli (https://github.com/webpack/webpack-cli)
We will use "npm" to install the CLI via "npm install -D webpack-cli".
Do you want to install 'webpack-cli' (yes/no):
The first time this happened I responded yes. After a flurry of installation and another try the same thing happened. However, vsce package has no trouble using webpack for a production build and pressing F5 to debug successfully puts together a development build in a dist folder with an unminified file I can examine (which is how I know the file mentioned below is being bundled).
Moving on from there I've modified webpack.config.js like so
//#ts-check
'use strict';
const path = require('path');
/**#type {import('webpack').Configuration}*/
const config = {
target: 'node', // vscode extensions run in a Node.js-context -> https://webpack.js.org/configuration/node/
entry: './src/extension.ts', // the entry point of this extension, -> https://webpack.js.org/configuration/entry-context/
output: {
// the bundle is stored in the 'dist' folder (check package.json), -> https://webpack.js.org/configuration/output/
path: path.resolve(__dirname, 'dist'),
filename: 'extension.js',
libraryTarget: 'commonjs2',
devtoolModuleFilenameTemplate: '../[resource-path]'
},
devtool: 'source-map',
externals: {
vscode: 'commonjs vscode' // the vscode-module is created on-the-fly and must be excluded. Add other modules that cannot be webpack'ed, -> https://webpack.js.org/configuration/externals/
},
resolve: {
// support reading TypeScript and JavaScript files, -> https://github.com/TypeStrong/ts-loader
extensions: ['.ts', '.js', '.css']
},
module: {
rules: [
{
test: /\.ts$/,
exclude: /node_modules/,
use: [
{
loader: 'ts-loader'
}
]
},
{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
}
]
}
};
module.exports = config;
As you can see there are rules and loaders for CSS.
When I add this import
import "../node_modules/highlight.js/styles/atelier-dune-light.css";
webpack happily builds the bundle and when I inspect it I can find the bundled CSS.
However, when I try to load the extension in the extension debug host, it fails to load, with this message.
Activating extension 'pdconsec.vscode-print' failed: document is not defined.
Enabling break on caught exceptions reveals this rather surprising exception.
Exception has occurred: Error: Cannot find module 'supports-color'
Require stack:
- c:\Users\Peter\AppData\Local\Programs\Microsoft VS Code\resources\app\node_modules.asar\get-uri\node_modules\debug\src\node.js
- c:\Users\Peter\AppData\Local\Programs\Microsoft VS Code\resources\app\node_modules.asar\get-uri\node_modules\debug\src\index.js
- c:\Users\Peter\AppData\Local\Programs\Microsoft VS Code\resources\app\node_modules.asar\get-uri\dist\index.js
- c:\Users\Peter\AppData\Local\Programs\Microsoft VS Code\resources\app\node_modules.asar\vscode-proxy-agent\out\agent.js
- c:\Users\Peter\AppData\Local\Programs\Microsoft VS Code\resources\app\node_modules.asar\vscode-proxy-agent\out\index.js
- c:\Users\Peter\AppData\Local\Programs\Microsoft VS Code\resources\app\out\bootstrap-amd.js
- c:\Users\Peter\AppData\Local\Programs\Microsoft VS Code\resources\app\out\bootstrap-fork.js
OK, so activation failed because the loader barfed. But WTF does importing CSS have to do with support-color?
Remove the import and it runs just fine. I really don't know how to respond to this; it's not clear to me why a demand for a stylesheet should cause that error. At this point I look to others for guidance and advice.
Remove style-loader from webpack.config.js to fix the error.
Pull the CSS as a string like this. Note the abbreviated path.
const cssPath: string = "highlight.js/styles/atelier-dune-light.css";
const theCss: string = require(cssPath).default.toString();
You do not need the import statement, the use of require will cause Webpack to bundle the files, but you still have to remove style-loader to avoid the loader error.

Webpack 1 with transform-loader?brfs on Windows 10

I am using the library pdfkit which uses fontkit which has hardcoded require('fs') in it, no matter if you use it on client or server.
Now the project is in React, using Webpack 1 for packaging. The regular overcoming of the problem is using the following webpack config.
module: {
loaders: [
{
test: /\.json$/,
loader: 'url-loader'
},
{
test: /node_modules\/(pdfkit|fontkit|png-js|linebreak|unicode-properties|brotli)\ //,
loader: "transform-loader?brfs"
},
],
},
This works great on MacOS, Linux and in Docker containers but now working on Windows 10 workstations. I could not find anyone having the same problem.
Edited the following does not resolve completely the case
I managed to overcome it by the following fake library hardcoded in the node_modules/fs/index.js file:
class fsClass {
readFileSync (file) {
return new Promise((resolve) => resolve('fake readFileSync: ' + file));
}
}
const fs = new fsClass();
module.exports = fs;
The project compiles it that way and is working but still I do not think this is the right decision. Any help/ideas would be appreciated!
Thanks!
So far, I have reached only one resolving of the problem. As suggested here:
https://github.com/foliojs/pdfkit/wiki/How-to-compile-standalone-PDFKit-for-use-in-the-browser
$ npm install browserify brfs
$ npm install pdfkit
$ node_modules/.bin/browserify --standalone PDFDocument node_modules/pdfkit/js/pdfkit.js > pdfkit.js
Then copy the file in the src folder and include pdfkit from there. The things work perfectly. The result is - precompiled library in the repo and a Webpack warning for that.
Still will be glad if anyone suggests a better decision

Express, Pug and Webpack

I have a Node js server app which uses Express and Pug. I would like to bundle it to single script which can be deployed by pm2. There seem to be several problems with this.
In runtime I get Cannot find module "." and during compilation few messages like
WARNING in ./node_modules/express/lib/view.js 80:29-41 Critical
dependency: the request of a dependency is an expression
appear which come from dynamic imports like require(mod).__express. I assume Webpack can't statically resolve those and does not know which dependency to include.
How can this be solved ?
How do I make Pug compile and be part of the output js ?
It is because webpack rebundle node_modules (already bundled) dependencies and in the case of pug, it doesn't work.
You need to use webpack-node-externals within the webpack externals option in order to specifically ask not to re-bundle depedencies.
Install webpack-node-externals: npm i -D webpack-node-externals
Integrate it your webpack config file:
Example
// ...
const nodeExternals = require('webpack-node-externals')
module.exports = {
target: 'node',
entry: {
// ...
},
module: {
// ...
},
externals: [nodeExternals()],
output: {
// ...
},
}

Webpack fails with Node FFI and Typescript - dynamic require error

In a simple Typescript program I require Node FFI with
import * as Electron from 'electron';`
import * as ffi from 'ffi';`
and then
mylib = ffi.Library('libmoi', {
'worker': [ 'string', [ 'string' ] ],
'test' : [ 'string', [] ]
} );
Linking that up via webpack yields
WARNING in ./~/bindings/bindings.js
Critical dependencies:
76:22-40 the request of a dependency is an expression
76:43-53 the request of a dependency is an expression
# ./~/bindings/bindings.js 76:22-40 76:43-53
The problem seems to be that FFI has a dynamic require and the fix seems to be to apply webpack.ContextReplacementPlugin in the webpack.config.js file.
This is a bit out of my reach, but an example for an Angular case is:
plugins: [
new webpack.ContextReplacementPlugin(
// The (\\|\/) piece accounts for path separators in *nix and Windows
/angular(\\|\/)core(\\|\/)(esm(\\|\/)src|src)(\\|\/)linker/,
root('./src') // location of your src
)
]
Any idea how to do this for FFI?
Here is the answer: github issue comment on the Johnny-Five repo
Quoting from brodo's answer, this is what you do to stop webpack getting snarled up with "bindings" and similar:
... the webpack config looks like this:
module.exports = {
plugins: [
new webpack.ContextReplacementPlugin(/bindings$/, /^$/)
],
externals: ["bindings"]
}
I also had a similar issue, somehow, I managed to resolve it. I will first explain my understanding.
Main work of webpack is to bundle the separate code file into one file, by default it bundles all the code that is referenced in its tree.
Generally two types of node_modules:
To be used on browser side(angular, rxjs etc)
To be used on nodejs side(express, ffi etc)
It is safer to bundle browser side node_module but not safer to bundle node side node_module because they are not designed like that So the solution is below two steps:
Give appropriate target(node, electron etc) in webpack.config.js file e.g "target":'electron-renderer' by default it is browser
Declare node_side module as external dependency in your webpack.config.js file e.g.
"externals": {
"bindings": "require('bindings')",
"ffi": "require('ffi')"
}

Resources