How can I get the application base path from a module in Node.js? - node.js

I'm building a web application in Node.js, and I'm implementing my API routes in separate modules. In one of my routes I'm doing some file manipulation and I need to know the base app path. If I use __dirname, it gives me the directory that houses my module of course.
I'm currently using this to get the base application path (given that I know the relative path to the module from base path):
path.join(__dirname, "../../", myfilename)
Is there a better way than using ../../? I'm running Node.js under Windows, so there isn't any process.env.PWD, and I don't want to be platform-specific anyway.

The approach of using __dirname is the most reliable one. It will always give you correct directory. You do not have to worry about ../../ in Windows environment as path.join() will take care of that.
There is an alternative solution though. You can use process.cwd() which returns the current working directory of the process. That command works fine if you execute your Node.js application from the base application directory. However, if you execute your Node.js application from different directory, say, its parent directory (e.g., node yourapp\index.js) then the __dirname mechanism will work much better.

You can define a global variable like in your app.js file:
global.__basedir = __dirname;
Then you can use this global variable anywhere. Like this:
var base_path = __basedir

You can use path.resolve() without arguments to get the working directory which is usually the base application path. If the argument is a relative path then it's assumed to be relative to the current working directory, so you can write
require(path.resolve(myfilename));
to require your module at the application root.

Related

AppImage from electron-builder with file system not working

I’m using Electron Builder to compile my Electron app to an .AppImage file, and I’m using the fs module to write to an .json file, but it’s not working in the appimage format (it’s working fine when I have the normal version not made with Electron Builder). I can still read from the file.
The code (preload):
setSettings: (value) => {fs.writeFileSync(path.join(__dirname, "settings.json"), JSON.stringify(value), "utf8")}
The code (on the website):
api.setSettings(settings);
The project: https://github.com/Nils75owo/crazyshit
That's not a problem with AppImage or Electron Builder but with the way you're packaging your app. Since you didn't post your package.json*, I can only guess what's wrong, but probably you haven't changed Electron Builder's default behaviour regarding packing your application.
By default, Electron Builder compiles your application, including all resources, into a single archive file in the ASAR format (think of it like the TAR format). Electron includes a patched version of the fs module to be able to read from the ASAR file, but writing to it is obviously not supported.
You have two options to mitigate this problem: Either you store your settings somewhere in the user's directory (which is the way I'd go, see below) or you refrain from packing your application to an ASAR file, but that will leave all your JavaScript code outside the executable in a simple folder. (Note that ASAR is not capable of keeping your code confidential, because there are applications which can extract such archives, but it makes it at least a little harder for attackers or curious eyes to get a copy of your code.)
To disable packing to ASAR, simply tell Electron Builder that you don't want it to compile an archive. Thus, in your package.json, include the following:
{
// ... other options
"build": {
// ... other build options
"asar": false
}
}
However, as I mentioned above, it's probably wiser to store settings in a common place where advanced users can actually find (and probably edit, mostly for troubleshooting) them. On Linux, one such folder would be ~/.config, where you could create a subdirectory for your application.
To get the specific application data path on a cross-platform basis, you can query Electron's app module from within the main process. You can do so like this:
const { app } = require ("electron"),
path = require ("path");
var configPath;
try {
configPath = path.join (app.getPath ("appData"), "your-app-name");
} catch (error) {
console.error (error);
app.quit ();
}
If you however have correctly set your application's name (by using app.setName ("...");), you can instead simply use app.getPath ("userData"); and omit the path joining. Take a look at the documentation!
For my Electron Applications, I typically choose to store settings in a common hidden directory (one example for a name could be the brand under which you plan to market the application) in the user's home directory and I then have separate directories for each application. But how you organise this is up to you completely.
* For the future, please refrain from directing us to a GitHub repository and instead include all information (and code is information too) needed to replicate/understand your problem in your question. That'd save us a lot of time and could potentially get you answers faster. Thanks!

Best practice to use a configuration file for Paths, Ports,

I have to update and optimize an nodejs Project for some new use cases. For this, i have to easily change the paths for different host systems. Till now they were hardcoded.
First i used global variables, just to get the system running. But globals are not a very clever idea. Now i created a config.js file which includes the paths and in any nodejs file i linked to them with request("config.js").
nodejs
`global.OEM_datapath = __dirname + '/public/data.csv';
now:
config.js
var PATHs = {
'OEM_datapath': __dirname + '/public/data/data.csv'
}
module.exports = PATHs;
other nodejs files:
var globals = require('./config');
console.log("path:" + globals.OEM_datapath);
'''
Is there a better way to use configuration settings? I considered to use process.env?
Node.js is an environment that helps you create server-side applications using JavaScript. One of the common Node.js elements that developers like and use are .env files. These files let you easily save and load environment variables. Developers often use them to store confidential information. However, sometimes they forget to disable access to these files from the outside, which can lead to major security problems. You can create a sperate env file and maintain the dynamic values.
I considered to use process.env
That is the standard way, in fact if you are working with containers, such as Docker this will also be the case. My suggestion would be to use YAML as the config language and derive config from that, again a Docker 'standard'.

How should I load a file that is inside my own module?

Current code in /config/index.js
const options = (require('js-yaml')).safeLoad(
(require('fs')).readFileSync(`./config/default-config.yaml`, "utf8"));
module.exports = options;
Works fine. Until I publish and use it in my other project. Then it's unable to find the file (naturally) as ./config/default-config.yaml doesn't exist in that project.
The only option I can think of involves checking to see if the file exists at that path, then trying to load it from node_modules/#company/alpha-gamma/config/default-config.yaml. This seems really hacky.
The config object is large, 200+ keys. I don't think it belongs in the code.
What's the best solution for loading a file that exists inside your module? I need to be able to load it for unit tests before publishing and load it at runtime when the library is required by another module.
Maybe the best alternative is to use json since I can then use the require module to load it in, instead of fs.
While I originally suggested utilizing __dirname as a valid option, I was wrong. Calling process.cwd() to fetch the application root and building the path off of that is the best approach.
As documented here:
Proper way to reference files relative to application root in Node.JS

How to add static files to an Electron app

How can I add JSON or TOML files in an Electron app for deployment? The following code works in development environment, but does not after packaging by electron-packager.
var presets = toml.parse(fs.readFileSync('presets.toml','utf8'));
According to the guide that took me way too long to find, the Electron team patched the fs module to provide a "virtual file system" under the root (/).
That means your file is accessible at fs.readFileSync('/presets.toml'); (notice the forward slash).
I went crazy till I found this one.
The problem is not adding it because it's already added.
Problem is finding base path after packaging.
So here it is:
const { app } = window.require('electron').remote;;
console.log(app.getAppPath());
Also, as you can see, if you're using React you need to use window.require instead of regular require (it throws nasty error otherwise).
Found about this here:
https://github.com/electron/electron/issues/3204#issuecomment-151000897

Where should I store cache of a custom CLI npm module?

I am developing an npm module, where user can interact with it through a terminal by executing commands:
> mymodule init
> mymodule do stuff
When executing certain commands user is being asked for some data, which will be used by the module. Since this data won't really change while using the module and since these commands can be executed pretty frequently, it is not the best option to ask user for the data any time he runs a command. So I've decided to cache this data, and as soon as it should live through multiple module calls, the easiest way to store it that I see is a file (the data structure allows to store it in a simple JSON). But I am to quite sure where should this file go on a user's machine.
Where in the file system should I store a cache in a form of a file or multiple files for a custom npm module, considering that the module itself can be installed globally, on multiple operation systems and can be used in multiple projects at the same time?
I was thinking about storing it in a module's folder, but it might be tricky in case of global installation + multi-project use. The second idea was to store it in OS specific tmp storage, but I am not quite sure about it too. I am also wondering if there are some alternatives to file storage in this case.
I would create a hidden folder in the user's home directory (starting with a dot). For instance, /home/user/.mymodule/config.cfg. The user's home directory isn't going anywhere, and the dot will make sure it's out of the user's way unless they go looking for it.
This is the standard way that most software stores user configs, including SSH, Bash, Nano, Wine, Ruby, Gimp, and even NPM itself.
On some systems you can cache to ~/.cache by create a sub-directory to store your cache data, though its much more common for applications to create a hidden directory in the users home directory. On modern windows machines you can use create a directory in C:/Users/someUser/AppData. In Windows using a . suffix will not hide a file. I'd recommend you do something platform agnostic like so:
var path = require('path');
function getAppDir(appName, cb) {
var plat = process.platform;
var homeDir = process.env[(plat == 'win32') ? 'USERPROFILE' : 'HOME'];
var appDir;
if(plat == 'win32') {
appDir = path.join(homeDir, 'AppData', appName);
}
else {
appDir = path.join(homeDir, '.' + appName);
}
fs.mkdir(appDir, function(err) {
if(err) return cb(err);
cb(null, appDir);
})
}
Just declare a function to get the app directory. This should handle most systems, but if you run into a case where it does not it should be easy to fix because you can just create some kind of alternate logic here. Lets say you want to allow a user to specify a custom location for app data in a config file later on, you could easily add that logic. For now this should suite most of your cases for most all Unix/Linux systems and Windows Vista and up.
Storing in system temp folder, depending on the system, your cache could be lost on an interval(cron) or on reboot. Using the global install path would lead to some issues. If you need this data to be unique per project, then you can extend that functionality to allow you to store this data in the project root, but not the module root. It would be best not to store it in the module root, even if its just installed as a local/project module, because then the user doesn't have the ability to include this folder in their repositories without including the entire module.
So in the event that you need to store this cached data relevant to a project, then you should do so in the project root not the node_modules. Otherwise store it in the users home directory in a system agnostic way.
First you need to know in what kind of SO you are running:
Your original idea is not bad, because global modules are not really global in all SO and in all virtual enviroments.
Using /home/user may not work in Windows. In windows you have to check process.ENV.HOMEPAHT
I recommend you a chain of checks to determine the best place.
Let the user take the control. Chose your own env variable. Supouse MYMOD_HOME. You firts check if process.ENV.MYMOD_HOME exists, and use it
Check if windows standard process.ENV.LOCALAPPDATA
Check if windows standard process.ENV.HOMEPATH
Check if exists '/home/user' or '~'
Otherwise use __dirname
In all cases create a directory ./mymodule

Resources