I am currently working on an Electron-App using NPM that unzips a file to a selected directory. An Installer basically. Now when I build the application it still works fine. I simply start the setup.exe in my win-unpacked folder and everything goes down smoothly. Now when I move the win-unpacked folder to a different directory my app runs fine but when it starts the unzipping process it throws the following error:
I have noticed, that the first file-path displayed (for some reason) doesnt use utf8, but all the others are being displayed correctly (should be an รค). Also I have allready tried deleting the node_modules folder and then running npm i to reinstall them. Still having the same issue.
The following is the code that starts the unzipping process:
const path = require('path');
const ipcRenderer = require('electron').ipcRenderer;
const Unzip = require('./unzip');
const os = require('os');
const fs = require('fs');
$('#information_next').click(function () {
var extractPath = $('#input_select').val();
let filepath;
const platform = os.platform();
const nodeName = platform == 'win32' ? 'wcd.node' : (platform == 'darwin' ? 'mcd.node' : 'lcd.node');
let customData = require("bindings")(nodeName);
let zip = h2a(customData.customData());
if(os.platform() == 'darwin') {
filepath = path.join(__dirname, '..', '..', '..', '..', 'ZIP.zip');
} else {
filepath = path.join(__dirname, '..', '..', 'ZIP.zip');
}
var xPath = path.join.apply(null, extractPath.split('\\'));
var unzip = new Unzip(filepath, xPath.toString());
unzip.extract(extractPath, zip, 100, (percentage) => {
// Code for Progressbar
finish = true;
setTimeout(function () {
$('.main_wrapper').addClass('hidden');
$('.main7').removeClass('hidden');
}, 1500);
}).catch((e) => {
$('.main6').addClass('hidden');
$('.main_install_error').prop('hidden', false);
});
});
Here I am using the bindings module to require a .node file which passes a string to my app. And this seems to be the module that causes the Error.
I have been trying to resolve this for hours now, but nothing I've found online works. Also I couldn't find anyone who had the same Error that I have. Any suggestions on how to fix this would be appreciated.
Greetings
Matt.S
EDIT:
I might have just figured out the main problem. The bindings module contains a function that looks for the module root. This path is displayed in the first line of the ERROR. However since this app has allready been built, all the source code is inside of an app.asar file. Bindings can't seem to distinguish between the .asar file and an ordinary folder. So even tho the path is correct, it doesn't work. The reason why it worked in the original win-unpacked is because bindings (if it cant find the module-root) moves up througth the directory until it finds the root. And since the original win-unpacked folder is inside my project directory bindings uses the un-built module-root.
I was able to reproduce this error with the bindings module.
It seems to have alot of problems with the electron framework which can cause such behaviour.
The 'root directory' Problem
You answered this yourself with your edit of the original question, but for the sake of delivering a complete answer, I am including this too
I simply start the setup.exe in my win-unpacked folder and everything goes down smoothly. Now when I move the win-unpacked folder to a different directory my app runs fine but when it starts the unzipping process it throws an error.
This has an interesting reason. Since your dist directory (the build destination of your project) is inside your working project, the bindings module assumes your working directory is the root directory of your built app. Thus it is able to resolve the path to your module and everything works fine. Once your built app is placed somewhere else, the bindings module isn't able to find the root directory of your app and throws the error you linked.
The 'file://' Problem
Another problem of the bindings module is handling paths with the 'file' protocol.
Someone already went ahead and created an issue (+ a pull request) for this problem, so you could modify your local installation of this module, even though I would discourage taking such actions.
My personal advice:
The current state of the bindings module makes it unattractive for use together with the Electron framework. I heard it even has problems handling umlauts properly, so your best bet in your specific situation is to get rid of it. You could make a small local module which wraps your .node binary and makes it as easy to require as all your other node modules.
This is an amazing article about creating your own module.
Related
I am creating a web component in Node.js. This component uses images, which I have put in a sub-directory within the component's directory.
When I import the image I get the error "Failed to load resource: the server responded with a status of 404 (Not Found)" in Chromium. I understand that the browser wants me to use the path from the root of the project to the file, but it kind of defeats the purpose of having a nice directory structure.
I also have Snowpack installed.
Edit: Using the Node.js package path is difficult, since Snowpack can't normally use packages not inside the browser, and I have to use Snowpack. With devOptions { polyfillNode: true } it should be possible, but I tried creating a snowpack.config.js file with that line to no avail. How do I implement it?
Or how do you usually handle paths in Snowpack? I am unsure about using a polyfill, and if there is another solution, I'd be happy to try it.
Relative paths can be used as :
//in ./app.js
const path = require('path');
//using path.relative()
const greet = require(path.relative(__dirname, './lib/greet.js'));
Same for image
// get the relative path of the image file from this file
const imagePath = path.relative(__dirname, './public/image.jpg');
// export a function that returns an HTML string with the image tag
module.exports = function() {
return `<img src="${imagePath}" alt="Image">`;
};
I have a question regarding Nodejs's path-handling.
I know that they have a page about that in the docs but it didnt contain what I needed.
So basically, I have a file that includes a relative path referencing a file (png in this case).
Now, based on where I call the file from, the picture is either found or not found (as the point in the fileSystem from where its called changes).
I am using the 'sharp' framework, 'sharp('./picture.png')' is similar to require.
Example:
File 'render.js' :
const pic = sharp('./picture.png')
Calling:
cmd\examplePath> node render.js //picture is found
cmd> node ./examplePath/render.js //picture is not found
The location of the picture relative to the file stays the same at all times!
My question now is if what I have described is to be expected from Nodejs or if there is something wrong. What would I need to do to be able to call the file from anywhere and have it still work?
Any tips are appreciated.
Thanks :)
Normally file handling in nodejs such a fs.open() just resolves a relative path versus the current working directory in nodejs. The current working directory will be whatever the OS working directory was when you started your nodejs app.
The current working directory is not necessarily the same as the directory where your script is located because the current working directory might be different than where your script is located when your nodejs program was started.
So, in your two command line examples, each is starting with a different current working directory, thus one works and one doesn't.
In general in nodejs, it is not advisable to rely on what the current working directory is because this lessens the ability to reuse your code in other projects where it might be loaded from a different directory. So, if the file you are trying to reference is in a known location relative to your script's file system location, then you would typically build a full path, using either __dirname (in CommonJS modules) or import.meta.url or import.meta.resolve() (in ESM modules).
For example, if the image is in the same directory as your script, then you could do this in a CommonJS module:
const path = require('path');
const fullPath = path.join(__dirname, "picture.png");
const pic = sharp(fullPath);
In an ESM module, you could do:
import path from 'path';
const __dirname = new URL('.', import.meta.url).pathname;
const fullPath = path.join(__dirname, "picture.png");
const pic = sharp(fullPath);
My question now is if what I have described is to be expected from Nodejs or if there is something wrong.
Yes, that is to be expected. Your are starting your program with different current working directories and have code that depends upon the current working directory being a certain value (because of your usage of a relative path).
What would I need to do to be able to call the file from anywhere and have it still work?
Build an absolute path using your script's directory as the anchor location as shown above.
Note that require() or import use completely different file searching logic and have some built-in behavior that is relative to the module's location that is running them. But, they are the exception, not the rule. Operations in the fs module (or other modules that use the fs module) use the current working directory as the base path if you supply a relative path.
I'm using Node.js (v16) dynamic imports in a project to load plugins using a function loadJsPlugin shown here:
import { pathToFileURL } from 'url';
async function loadJsPlugin(pluginPath) {
const pluginURL = pathToFileURL(pluginPath).toString();
const result = await import(pluginURL);
return result.default;
}
My main program provides absolute paths to the loadJsPlugin function, such as /home/sparky/example/plugins/plugin1.js (Linux) or C:\Users\sparky\example\plugins\plugin1.js (Windows). The pathToFileURL function then converts these absolute paths to URLs like file:///home/sparky/example/plugins/plugin1.js (Linux) or file:///C:/Users/sparky/example/plugins/plugin1.js (Windows).
Loading the plugins this way works fine when the loadJsPlugin function is in the same package as the main program, like this:
import { loadJsPlugin } from './plugin-loader.js';
async function doSomething() {
const plugin = await loadJsPlugin('...'); // works
// use plugin
}
However, if I try to move loadJsPlugin to a separate library and use it from there, it fails with Error: Cannot find module '<url here>'
import { loadJsPlugin } from '#example/plugin-loader';
async function doSomething() {
const plugin = await loadJsPlugin('...'); // error
// use plugin
}
NOTE: the dependency name here is not on NPM, it's on a private repository and there's no problem loading the dependency itself. Also, static ES6 imports in general are working fine in this system.
I looked through Node.js documentation, MDN documentation, and other StackOverflow questions for information about what is allowed or not, or whether dynamic import works differently when in the same package or a dependency, and didn't find anything about this. As far as I can tell, if a relative path or file URL is provided, and the file is found, it should work.
Ruling out file not found:
I can switch back and forth between the two import lines to load the loadJsPlugin function from either ./plugin-loader.js or #example/plugin-loader, give it the same input, and the one in the same package works while the one from the dependency doesn't.
When I test in VS Code, I can hover the mouse over the URL in the Error: Cannot find module 'file:///...' message and the file opens just fine
I can also copy the 'file:///...' URL to a curl command (Linux) or paste it into the address bar of Windows Explorer and it works.
If I try a path that actually doesn't exist, I get a slightly different message Error [ERR_MODULE_NOT_FOUND]: Cannot find module '<path here>', and it shows the absolute path to the file that wasn't found instead of the file URL I provided.
Checking different file locations:
I tried loading plugins that are located in a directory outside the program (the paths shown above like /home/sparky/example/plugins/...); got the results described above
I tried loading plugins that are located in the same directory (or subdirectory) as the main program; same result
I tried loading plugins that are packaged with the dependency in node_modules/#example/plugin-loader; same result (obviously this is not a useful set up but I just wanted to check it)
I'd like to put the plugin loader in a separate library instead of having the same code in every project, but it seems that dynamic import only works from the main package and not from its dependencies.
I'm hoping someone here can explain what is going on, or give me a pointer to what might make this work.
I have a NodeJS module that is supposed to translate an XML to HTML via an XSL file. The problem is I need to bundle the XSL with the rest of the module.
Right now it's inside the source folder and I use it via:
function createHtml(xml) {
const fs = require('fs');
const xsltProcessor = require('xslt-processor');
var xsl = fs.readFileSync('./src/xsl/html-export.xsl', 'utf-8');
return xsltProcessor.xsltProcess(xsltProcessor.xmlParse(xml), xsltProcessor.xmlParse(xsl));
}
Of course I made sure to have this folder in the files property of the package.json:
"files": [
"src"
],
This setup works inside the module, but not when I publish the module and require it from a second module. Then I need to copy the XSL file into the second module's path as well.
That's not really working for me. I want to access the file relative to the JavaScript source file, not the current NodeJS application.
I tried using a different kind of path (html-export.xsl, ./html-export.xsl, ./xsl/html-export.xsl) but nothing else works even inside the module. So I can't publish it to try it out elsewhere.
The good news is: the XSL file is actually included inside the module during the publishing; when I install it the module in the node_modules/ folder contains the XSL. So it's "just" the above code that's not working, because the path is resolved relative to the second module (i.e. the one with the require), not relative to the original one (i.e. the one inside the node_modules/).
How do I handle assets in NodeJS so they are bundled with the module they belong to?
This question seems related, but the answer is exactly what I did, and it doesn't work for me for some reason.
It appears you only want to use the .xsl file internally in your package, which is something that I originally misunderstood..
In order to accomplish this, you can use the path package to resolve the path of your .xsl file.
Like this:
function createHtml(xml) {
const fs = require('fs');
const path = require('path');
const xsltProcessor = require('xslt-processor');
var xsl = fs.readFileSync(path.resolve(__dirname, './src/xsl/html-export.xsl'), 'utf-8');
return xsltProcessor.xsltProcess(xsltProcessor.xmlParse(xml), xsltProcessor.xmlParse(xsl));
}
In order to demonstrate how to do this, I have built an example NPM package for you to review. While this package doesn't use an .xsl file, it uses a .txt file - the logic remains the same.
The readme file within the package contains instructions on how to use it..
You can find the package on NPM here
You can find the source code to the package here
The idea
First you have a main Nuxt website like any other. Then you add my module to your project. My module then adds a subdomain "admin.example.com" to your project which is a fully fleshed out Nuxt based website all of its own but running from your projects process, so instead of making two website projects that have to be started separately the idea is that I can make my one website project and then one module project which adds the other website, turning three projects into two.
Code
module.js
this.addServerMiddleware(vhost('admin.website.com', adminApp));
adminApp.js
const {Nuxt, Builder} = require('nuxt');
const config = require('../admin/nuxt.config');
config.dev = true;
const nuxt = new Nuxt(config);
if (nuxt.options.dev) {
new Builder(nuxt).build()
.catch(function(error) {
console.log(error);
})
}
module.exports = nuxt.render;
Part of nuxt.config.js from the admin website
// __dirname leads to my modules directory
// process.cwd() leads to the main websites folder
module.exports = {
mode: 'universal',
srcDir: __dirname,
rootDir: __dirname,
modulesDir: process.cwd(),
The problem
Whenever I go to "admin.website.com" it results in the following message:
I've tried tonnes various changes and tweaks already but nothing seems to be working. As far as I can tell it might have to do with the fact that either A) the website gets built somewhere it doesn't find the generated resource files, or B) that I haven't prayed enough to the spaghetti monster.
I've tried exporting adminApp as an Express app that uses app.use(nuxt.render) but that results in the same error so I'm certain that's not the issue. I've tried using an Express app for api.website.com and that works just fine, so I know I'm like THIS |--| close to making this all work. Adding subdomains like this does work and each subdomain can be an Express app, but for some reason Nuxt can't find any resources when used this way. According to https://nuxtjs.org/api/nuxt-render/ nuxt.render(req, res) as you can see takes req and res as arguments, which should mean I don't need to use it via an Express app, serverMiddleware is already an Express app.
Thoughts?
I ran in this error before when serving apps through serverless. I think that you need to explicitly set a publicPath or a buildDir in nuxt.config.js for both apps to make setup work. Your problem could be that all the build assets are generated in the same folder, causing one of the apps to not find the build assets. The buildDir property is set on the nuxt.config.js and the publicPath setting is defined on the build property.
More info here:
https://nuxtjs.org/api/configuration-builddir
https://nuxtjs.org/api/configuration-build#publicpath