Get installed package root path from within package - javascript

I am building an npm package which is basically an CLI tool.
It is intended to be installed globally and used as CLI with dist/clj.js, but can also be installed as a regular dependency and accessed via dist/index.js.
From inside .js files of my package (like dist/cli.js or dist/index.js) I want to get an absolute path to the root of my package (where package.json is located).
I know this can be done with __dirname but for different files in my project it require different actions:
// dist/cli.js
console.log(require('path').join(__dirname, '..'));
// dist/a/b/c/foo.js
console.log(require('path').join(__dirname, '..', '..', '..', '..'));
Is there a better way to get the root of both globally or locally installed package from any .js file inside this package regardless of the folder this package is used in?

This code seems to work from any file within package:
const path = require('path');
let packageRootPath = path.join(path.dirname(require.main.filename), '..');
.. in the end needed to jump from src/dist to the root since all my js files are located in src/dist forders.

Related

How can I set production build path in vue-cli?

I'm using the vue-cli tool to develop vuejs apps, and I'm developing a server too, so i want to build the /dist folder in my server to send it to the browser as static, but I don't even know where the /dist folder is.
There is a webpack.prod.config.js file (I think is the webpack config for production) and in the path-output field I tried to wirte the path but didn't work.
/dist is just a default setting by VueJS. If you want to modify this : as the documentation from VueJS states :
Always use outputDir instead of modifying webpack output.path.
Create a vue.config.js as an optional config file that will be automatically loaded by #vue/cli-service if it's present in your project root (next to package.json).
The file should export an object containing options:
// vue.config.js
module.exports = {
// options...
}
As otheres already stated in the comments, use outputDir to specify the directory where the production build files will be generated in when running vue-cli-service build. Note the target directory will be removed before building (this behavior can be disabled by passing --no-clean when building).
// vue.config.js
module.exports = {
outputDir : ''
}

Vue "npm run build" Ignores vue.config.js File

I have a Webpack-templated Vue project, initiated through vue-cli.
I have created a simple 'vue.config.js' file stored in the root folder (where package.json is at) containing the following:
// vue.config.js
module.exports = {
productionSourceMap: false
}
Though when building the project using "npm run build" it ignores it.
I have tried different configurations to check if the problem is with the file or the setting, and the problem is with the file.
I am using webpack#3.12.0, vue#2.6.11, #vue/cli 4.2.3 and npm#6.9.0.
Make sure your build confiuration (in your case the webpack build configs) include your file.
Generally, you will have a source folder (often src) and the builder will build all the files in that dir only. Then you have your destination directory (often dist or build) where your build files will be stored.
Two solutions:
add your conf file to the build source.
move your vue.conf.js file into your source directory
For some reason, I did not manage to get vue.config.js to work.
Alternatively, I edited my webpack config, which as my build files mentioned was located at /config/index.js
Then, I proceeded to pass my build configurations to the build parameter which already appears on the file.
build: {
...
}
And it worked. I assume it may be because I used npm run dev instead of the vue-service-cli, so webpack did not go through the vue.config.js file.

How do I require something in root project directory from inside node package library?

I wanna create a node package modules, but I have difficulty to require a file from root project directory to use inside my node package module I created.
If I have directory structure like this
- node_modules
- library_name
- lib
- index.js
- bin
- run.sh
- config.js
If the run.sh called, it will run index.js. Inside index.js, how do I resolve to root directory which later I can require config.js inside index.js?
Package binary can accept configuration path explicitly as an argument.
If package binary doesn't run as NPM script, it shouldn't rely on parent project structure.
If package binary runs via NPM script:
"scripts": {
"foo": "library_name"
}
This will set current working directory to project root, so it could be required as:
const config = require(path.join(process.cwd(), 'config'));
Both approaches can be combined; this is often used to provide configuration files with default locations to third-party CLI (Mocha, etc).
If you're in index.js and config.js is in the directory above node_modules in your diagram, then you can build a path to config.js like this:
const path = require('path');
let configFilename = path.join(__dirname, "../../../", "config.js");
__dirname is the directory that index.js is in.
The first ../ takes you up to the library_name directory.
The second ../ takes you up to the node_modules directory.
The third ../ takes you up to the parent of node_modules (what you call project root) where config.js appears to be.
If you really want your module to be independent of how it is installed or how NPM might change in the future, then you need to somehow pass in the location of the config file in any number of ways:
By making sure the current working directory is set to the project root so you can use process.cwd() to get access to the config file.
By setting an environment variable to the root directory when starting your project.
By passing the root directory in to a module constructor function.
By loading and passing the config object itself in to a module constructor function.
I create module same your module.
And I call const config = require('../config'), it work.

How to manage configuration for Webpack/Electron app?

I am using Webpack 2 and Electron to build nodejs application on Mac.
In my project in the root I have directory 'data' where I store configuration in a json like data/configurations/files.json (in practices there are different files with dynamic names)
After webpackaing though when I call: fs.readdirSync(remote.app.getAppPath()); to get files in the root I get only these packed: [ "default_app.js", "icon.png", "index.html", "main.js", "package.json", "renderer.js" ]
path.join(remote.app.getAppPath(), 'data/tests/groups.json'); called with FS ReadSync leads to an issue Error: ENOENT, data/tests/groups.json not found in /Users/myuser/myproject/node_modules/electron/dist/Electron.‌​app/Contents/Resourc‌​es/default_app.asar. So it seems that the whole data folder is not picked up by webpacker.
Webpack config is using json-loader and I did not find any documentation mentioning anything special about including specific files or jsons. Or do I have to reference json files in my code differently as they might be packed under main.js.
What is the best practice for Electron/Webpack for managing JSON config files? Am I doing something wrong when webpacking the project?
My project is based of https://github.com/SimulatedGREG/electron-vue using webpack/electron/vue
The Webpack Misconception
One thing to understand upfront is that webpack does not bundle files required through fs or other modules that ask for a path to a file. These type of assets are commonly labeled as Static Assets, as they are not bundled in any way. webpack will only bundle files that are required or imported (ES6). Furthermore, depending on your webpack configuration, your project root may not always match what is output within your production builds.
Based on the electron-vue documentation's Project Structure/File Tree, you will find that only webpack bundles and the static/ directory are made available in production builds. electron-vue also has a handy __static global variable that can provide a path to that static/ folder within both development and production. You can use this variable similar to how one would with __dirname and path.join to access your JSON files, or really any files.
A Solution to Static Assets
It seems the current version of the electron-vue boilerplate already has this solved for you, but I'm going to describe how this is setup with webpack as it can apply to not only JSON files and how it can also apply for any webpack + electron setup. The following solution assumes your webpack build outputs to a separate folder, which we'll use dist/ in this case, assumes your webpack configuration is located in your project's root directory, and assumes process.env.NODE_ENV is set to development during development.
The static/ directory
During development we need a place to store our static assets, so let's place them in a directory called static/. Here we can put files, such as JSONs, that we know we will need to read with fs or some other module that requires a full path to the file.
Now we need to make that static/ assets directory available in production builds.
But webpack isn't handling this folder at all, what can we do?
Let's use the simple copy-webpack-plugin. Within our webpack configuration file we can add this plugin when building for production and configure it to copy the static/ folder into our dist/ folder.
new CopyWebpackPlugin([
{
from: path.join(__dirname, '/static'),
to: path.join(__dirname, '/dist/static'),
ignore: ['.*']
}
])
Okay so the assets are in production, but how do I get a path to this folder in both development and production?
Creating a global __static variable
What's the point of making this __static variable?
Using __dirname is not reliable in webpack + electron setups. During development __dirname could be in reference to a directory that exists in your src/ files. In production, since webpack bundles our src/ files into one script, that path you formed to get to static/ doesn't exist anymore. Furthermore, those files you put inside src/ that were not required or imported never make it to your production build.
When handling the project structure differences from development and production, trying to get a path to static/ will be highly annoying during development having to always check your process.env.NODE_ENV.
So let's simplify this by creating one source of truth.
Using the webpack.DefinePlugin we can set our __static variable only in development to yield a path that points to <projectRoot>/static/. Depending if you have multiple webpack configurations, you can apply this for both a main and renderer process configuration.
new webpack.DefinePlugin({
'__static': `"${path.join(__dirname, '/static').replace(/\\/g, '\\\\')}"`
})
In production, we need to set the __static variable manually in our code. Here's what we can do...
index.html (renderer process)
<!-- Set `__static` path to static files in production -->
<script>
if (process.env.NODE_ENV !== 'development') window.__static = require('path').join(__dirname, '/static').replace(/\\/g, '\\\\')
</script>
<!-- import webpack bundle -->
main.js (main process)
// Set `__static` path to static files in production
if (process.env.NODE_ENV !== 'development') {
global.__static = require('path').join(__dirname, '/static').replace(/\\/g, '\\\\')
}
// rest of application code below
Now start using your __static variable
Let's say we have a simple JSON file we need to read with fs, here's what we can accomplish now...
static/someFile.json
{"foo":"bar"}
someScript.js (renderer or main process)
import fs from 'fs'
import path from 'path'
const someFile = fs.readFileSync(path.join(__static, '/someFile.json'), 'utf8')
console.log(JSON.parse(someFile))
// => { foo: bar }
Conclusion
webpack was made to bundle assets together that are required or imported into one nice bundle. Assets referenced with fs or other modules that need a file path are considered Static Assets, and webpack does not directly handle these. Using copy-webpack-plugin and webpack.DefinePlugin we can setup a reliable __static variable that yields a path to our static/ assets directory in both development and production.
To end, I personally haven't seen any other webpack + electron boilerplates handle this situation as it isn't a very common situation, but I think we can all agree that having one source of truth to a static assets directory is a wonderful approach to alleviate developer fatigue.
I think the confusion, (if there is any), might come from the fact that webpack not only "packs", embeds, things, code, etc... but also process content with its plugins.
html plugin being a good example, as it simply generates an html file at build-time.
And how this relates to the config file issue?,
well depending on how you are "requiring" the "config" file, what plug-in you are using to process that content.
You could be embedding it, or simply loading it as text, from file system or http, or else...
In the case of a config file, that I guess you want it to be parsed at runtime,
otherwise it's just fancy hardcoding values that perhaps you could be better simply typing it in your source code as simple objects.
And again in that case I think webpack adds little to nothing to the runtime needs, as there is nothing to pre-pack to read at later use,
so I would possibly instead or "require"it, i'll read it from the file system, with something like :
// read it parse it relative to appPath/cwd,
const config = JSON.parse(
fs.readfileSync(
path.join( app.getAppPath(), "config.json" ),
"utf-8"
))
//note: look fs-extra, it does all that minus the app.path plus async
and electron will read it from the file system , or if using Electron.require will read it from asar|fileSystem (in that order if I remember correctly, I could be wrong),
Webpack design philosophy is focused around very simple yet powerful concept:
Transform and bundle everything that is actually used by your app.
To achieve that webpack introduces a powerful concept of dependency graph, which is able to manage virtually any kind of dependencies (not only *.js modules) by the means of so-called loaders.
The purpose of a loader is to transform your dependency in a way that makes statement import smth from 'your_dependency' meaningful. For instance, json-loader calls JSON.parse(...) during loading of *.json file and returns configuration object. Therefore, in order to take advantage of webpack dependency resolution system for managing JSONs, start from installing json-loader:
$ npm install --save-dev json-loader
Then modify your webpack.config.js in the following way:
module.exports = {
...
module: {
rules: [
{test: /\.json$/, use: 'json-loader'}
]
}
...
};
At this point webpack should be able to resolve your JSON dependencies by their absolute paths, so the following should work (I assume here that you have a subdirectory config of your root context dir, containing file sample.json):
import sampleCfg from './config/sample.json';
But importing physical paths doesn't lead to elegant, robust and maintainable code (think of testability, for example), so it is considered a good practice to add aliases to your webpack.config.js for abstracting away your physical .config/ folder from your import statements
module.exports = {
...
resolve: {
alias: {
cfg: './config'
}
}
...
}
Then you'll be able to import your JSON config like that:
import sampleCfg from 'cfg/sample.json'
Finally, if you use SimulatedGREG/electron-vue Electron project template (as you mentioned in your post), then you have three webpack configuration files:
.electron-vue/webpack.web.config.js - use this config file if you use this template just for ordinary web development (i.e. not for building native Electron projects);
.electron-vue/webpack.main.config.js - use this file to configure webpack module that will run inside Electron's main process;
.electron-vue/webpack.renderer.config.js - use this file for Electron's renderer process.
You can find more information on main and renderer processes in the official Electron documentation.

How to get the absolute path of my NPM global package?

I'm building an NPM package that is supposed to run globally.
Inside the NPM package, there's a data folder which contains a file I need to read at runtime.
How can I get the absolute path of my NPM package, so I can read the data folder?
I'm searching for that on Google with no luck.
You can use the path module and with it either start your paths with a ./ or just omit any / at the begining of a string.
for example:
var path = require('path');
console.log(path.resolve('./');
is the same as
var path = require('path');
console.log(path.resolve('');
the resolve method is nice in that it'll resolve your relative path with an absolute path to it. Good to be safe!
To build on #explosion pills' comment, it's nice to combine __dirname and path.resolve
console.log(path.resolve(__dirname));

Categories

Resources