fs doesn't get file if i publish module to npm - node.js

I'm facing a problem when i publish my module to npm.
Here's the file structure:
- node_modules
- lib
-- file.txt
- index.js
- package.json
I use it like that:
var a = fs.readFileSync('./lib/file.txt');
It works in local development. But when i publish my module to npm, it says:
Error: ENOENT, no such file or directory './lib/file.txt'
This error comes from fs, it doesn't get file. Is it about nested node_modules folder?
I mean when i want to test my module in clear folder, i install module:
npm install <my-module-name>
Creating a test.js file and puting that codes inside it:
var myModuleName = require('my-module-name');
var a = myModuleName();
console.log(a);
So new structure become:
- node_modules
-- my-module-name
-- node_modules
-- lib
-- file.txt
-- index.js
-- package.json
- test.js
Any idea?

This is because fs.readFileSync is looking for the file relative to the current working directory. Try using __dirname:
var path = require('path');
var a = fs.readFileSync(path.join(__dirname, 'lib', 'file.txt'));

Related

Finding the root directory of a dependency in NPM

Let's say I have installed awesome-package inside my-app, and let's say the structure looks like:
my-app/
node_modules/
awesome-package/
node_modules/
another-package/
static/
index.js
dist/
index.js
dist/
index.js
Inside my-app/index.js I do require('awesome-package'). Now I want to get the root directory of awesome-package, so I can basically fs.readFileSync something from another-package
How can I get the root directory of a script?
I think require.resolve can be used to achieve that. It will give you the full path to the module. Getting the root directory from that should be easy.
const path = require('path');
let loc = require.resolve('awesome-package');
// most likely something like the following depends on the package
// /path/to/my-app/node_modules/awesome-package/static/index.js
console.log(loc);
// something like the following will give you the root directory
// of the package
console.log(path.join(
loc.substring(0,a.lastIndexOf('node_modules')),
'node_modules',
'awesome-package'
));
Accepted answer doesn't work on esm package.
Accepted answer return cjs path.
Answer for esm path
const fullPath = await import.meta.resolve("package_name");
console.log(fullPath);
need to add flag: --experimental-import-meta-resolve

How to get the root of project which installed my npm module?

I am developing an NPM package. Now I want to know the easiest way to get the root of the project which utilizes my custom package.
Within my package itself it is pretty easy to get the root in node.js like so:
__dirname
This points to the root of my package itself though. I would like to get the root of the project which actually performed the npm install of my package though.
Obviously, I could simply go up from the node_modules directory and assume that the root directory is there but this sounds really bad.
Is there a standard way to achieve this?
you could use app-root-path which is an npm module and its pretty neat.
npm i -S app-root-path
after that just do
var reqlib = require('app-root-path').require;
var myModule = reqlib('/lib/my-module.js');
You can use the info from: require.main
The Module object representing the entry script loaded when the
Node.js process launched. See "Accessing the main module".
const path = require('path');
console.log(path.dirname(require.main.filename));
Having this folder structure:
/app
entry.js
node_modules/
my-package/
index.js
app/entry.js
const myPackage = require('my-package');
app/node_modules/my-package/index.js
const path = require('path');
console.log(path.dirname(require.main.filename));
And when you call: node entry.js
You will get: /app printed to stdout.
KISS
const fullPath = path.dirname(require.main.filename);
const regexResp = /^(.*?)node_modules/.exec(fullPath);
const appRoot = regexResp ? regexResp[1] : fullPath;
console.log(appRoot); // outputs the directory which is the root of this project

How get path to folder "node_modules" in js file?

Node.js - 8.2.1
I my index.js I hard code path to node_modules:
'use strict';
var NPM_NODE_MODULES_BASE_DIR = 'd:\\Programs\\nodejs\\node_modules\\npm\\node_modules\\'
var express = require(NPM_NODE_MODULES_BASE_DIR + '\express'),
bodyParser = require(NPM_NODE_MODULES_BASE_DIR +'body-parser');
var app = express();
app.use(bodyParser.urlencoded({extended: true}));
But it's not very good, because code is not portable. How I can get path to folder node_modules of Node.js ?
Instead of installing npm modules globally you could install them in your src folder, whenever you are in the src folder and do an npm install it should be installed in the local npm modules folder in the src file making it portable, you could use --save to add it to your dependencies so you can get it while initialising your node project
Here is an excerpt from the nodejs current master branch source documentation [doc/api/modules.md]...
...high-level algorithm in pseudocode of what require.resolve() does:
require(X) from module at path Y
If X is a core module,
a. return the core module
b. STOP
If X begins with '/'
a. set Y to be the filesystem root
If X begins with './' or '/' or '../'
a. LOAD_AS_FILE(Y + X)
b. LOAD_AS_DIRECTORY(Y + X)
LOAD_NODE_MODULES(X, dirname(Y))
THROW "not found"
Please note that you can have more than one node_modules (see https://docs.npmjs.com/files/folders).
Most of the times, the node_modules you want is the parent path of the directory your current module lives in:
const path = require('path').resolve(__dirname, '..');

Requiring a project file from an npm module

I have an npm module that requires a config file (provided by the user).
Is there a way to tell that module to look for a specific file in the user's directory ?
I want something like this:
In the module
const config = require(USER_ROOT_DIRECTORY + '/config.json');
In the user's project
/node_modules (contains the module file)
/src/index.js
/config.json
In the index.js file
const module = require('module'); // will automatically get the config.json file's content
You can use absolute path since you know with npm module requires what file simply add the path inside the module file better to add in index.js example-
var config = require('./subfolder/config.json')

How to share code between node.js apps?

I have several apps in node that all share a few modules that I've written. These modules are not available via npm. I would like to be able to share freely between apps, but I don't want to copy directories around, nor rely on Git to do so. And I'm not really big on using symlinks to do this either.
I would like to arrange directories something like this:
app1
server.js
node_modules
(public modules from npm needed for app1)
lib
(my own modules specific to app1)
app2
server.js
node_modules
(public modules from npm needed for app2)
lib
(my own modules specific to app2)
shared_lib
(my own modules that are used in both app1 and app2)
The problem I'm seeing is that the modules in shared_lib seem to get confused as to where to find the modules that will be in the node_modules directory of whichever app they are running in. At least I think that is the problem.
So....what is a good way to do this that avoids having duplicates of files? (note that I don't care about duplicates of things in node_modules, since those aren't my code, I don't check them into Git, etc)
The npm documentation recommends using npm-link to create your own Node.js packages locally, and then making them available to other Node.js applications. It's a simple four-step process.
A typical procedure would be to first create a package with the following structure:
hello
| index.js
| package.json
A typical implementation of these files would be:
index.js
exports.world = function() {
return('Hello World');
}
package.json
{
"name": "hello",
"version": "0.0.1",
"private": true,
"main": "index.js",
"dependencies": {
},
"engines": {
"node": "v0.6.x"
}
}
"private:true" ensures that npm will refuse to publish the package. This is a way to prevent accidental publication of private packages.
Next, navigate to the root of your Node.js package folder and run npm link to link the package globally so it can be used in other applications.
To use this package in another application, e.g., "hello-world", with the following directory structure:
hello-world
| app.js
Navigate to the hello-world folder and run:
npm link hello
Now you can use it like any other npm package like so:
app.js
var http = require('http');
var hello = require('hello');
var server = http.createServer(function(req, res) {
res.writeHead(200);
res.end(hello.world());
});
server.listen(8080);
I've got this working by having node_modules folders at different levels - node then automatically traverses upwards until it finds the module.
Note you don't have to publish to npm to have a module inside of node_modules - just use:
"private": true
Inside each of your private package.json files - for your project I would have the following:
app1
server.js
node_modules
(public modules from npm needed for app1)
(private modules locally needed for app1)
app2
server.js
node_modules
(public modules from npm needed for app2)
(private modules locally needed for app2)
node_modules
(public modules from npm needed for app1 & app2)
(private modules locally for app1 & app2)
The point is node.js has a mechanism for dealing with this already and it's awesome. Just combine it with the 'private not on NPM' trick and you are good to go.
In short a:
require('somemodule')
From app A or B would cascade upwards until it found the module - regardless if it lived lower down or higher up. Indeed - this lets you hot-swap the location without changing any of the require(...) statements.
node.js module documentation
Just use the correct path in your require call
For example in server.js that would be:
var moduleName = require('../shared_lib/moduleName/module.js');
Its Important to know that as soon as your path is prefixed with '/', '../', or './' the path is relative to the calling file.
For further information about nodes module loading visit:
http://nodejs.org/docs/latest/api/modules.html
Yes, you can reference shared_lib from app1, but then you run into a problem if you want to package and deploy app1 to some other environment, such as a web server on AWS.
In this case, you're better off installing your modules in shared_lib to app1 and app2 using "npm install shared_lib/module". It will also install all the dependencies of the shared_lib modules in app1 and app2 and deal with conflicts/duplicates.
See this:
How to install a private NPM module without my own registry?
If you check out the node.js docs, you'll see that Node.js understands the package.json file format as well, at least cursorily.
Basically, if you have a directory named foo, and in that directory is a package.json file with the key-value pair: "main": "myCode.js", then if you try to require("foo") and it finds this directory with a package.json file inside, it will then use foo/myCode.js for the foo module.
So, with your directory structure, if each shared lib has it's own directory with such a simple package.json file inside, then your apps can get the shared libs by:
var lib1 = require('../shared_lib/lib1');
var lib2 = require('../shared_lib/lib2');
And that should work for both of these apps.
Another solution can be cloning files from the other places into this repo:
clone.js:
const path = require('path')
const fs = require('fs')
const shared = [
{
type: 'file',
source: '../app1',
files: [
'src/file1',
'src/file2',
'...'
],
},
]
function cloneFiles(source, files) {
const Reset = '\x1b[0m'
const FgGreen = '\x1b[32m'
console.log(`---------- Cloning ${files.length} files from ${source} ----------`)
for (const file of files) {
const sourceFile = path.join(__dirname, '..', source, file)
const targetFile = path.join(__dirname, '..', file)
process.stdout.write(`📁 ${file} ... `)
fs.copyFileSync(sourceFile, targetFile)
console.log(`${FgGreen}Done!${Reset}`)
}
console.log(`---------- All done successfully ----------\n`)
}
;(() => {
for (const item of shared) {
switch (item.type) {
case 'file':
cloneFiles(item.source, item.files)
break
}
}
})()
Then, in the package.json you can add this script and call it when you want to clone / sync files:
"clone": "node clone.js"

Resources