Why Node.js only recognizes absolute paths? [duplicate] - javascript

This question already has answers here:
What is the difference between __dirname and ./ in node.js?
(2 answers)
Closed 3 years ago.
I created a file called nodes, then initialized the file with npm init and the main js file is called main.js. I also created index.html and index.css in the file, after that I want to use Node.js Render this index.html, so I wrote in main.js:
const http = require('http');
const fs = require('fs');
const hostname = '127.0.0.1';
const port = 9000;
const mainHTML = './index.html';
const server = http.createServer((req, res) => {
fs.stat(`./${mainHTML}`, (err, stats) => {
if(stats) {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/html');
fs.createReadStream(mainHTML).pipe(res);
}
});
});
server.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});
I opened the server with the node desktop/nodes command, but node.js could not find the file.
Until I changed the relative path to an absolute path, Node.js will recognize it:
const mainHTML = 'desktop/nodes/index.html';
Why is this? If I want to use a relative path, how do I do it?

When you access a file in node.js with a relative path, the file is accessed relative to the value of the current working directory for the process. Note, in the modular world of node.js, the current working directory may or may not be the same as the directory where your module was located. And, your code can change the current working directory to be whatever you want it to be.
It is common in modular node.js code to have a programming desire to access things relative to the directory where the current module's code was loaded from. This gives you the ability to use relative paths so the app/module can work anywhere, but it gives you certainty that you'll get the files you want. To do this, one typically uses the module-specific variable __dirname. This is the directory that the current module was loaded from. If it's the main script that node.js was started with then, it's the directory of that script.
So, to get a file from the same directory as the script you are current in, you would do this:
const mainHTML = 'index.html';
fs.createReadStream(path.join(__dirname, mainHTML)).pipe(res);
To access a file in a subdirectory public below where the script is, you could do this:
const mainHTML = 'public/index.html';
fs.createReadStream(path.join(__dirname, mainHTML)).pipe(res);
To access a file in a different subdirectory at the same level (common parent directory) as where the script is, you could do this:
const mainHTML = '../public/index.html';
fs.createReadStream(path.join(__dirname, mainHTML)).pipe(res);
All of these use paths that are relative to where the script itself is located and do not depend upon how the module/script was loaded or what the current working directory of the app is.

You are creating http server, which creates it's path as base, so it understands only paths taking that base path as relative path. If you want to use relative path, then you need to resolve that path.
You can use 'path' library.
const path = require('path')
// To resolve parent path
path.resolve('..', __dirname__)

Related

How do I access a JSON file relative to my calling module in Node?

I'm defining a package, PackageA, that has a function (parseJson) that takes in a file path to a json file to parse. In another package, PackageB, I want to be able to call PackageA using a file I specify with a local path from PackageB. For example, if file.json is in the same directory as packageB, I'd like to be able to call PackageA.parseJson('./file.json'), without any extra code in PackageB. How would I do this? It seems that require requires a path from PackageA to the file, which is not what I want.
Edit: Currently, parseJson looks something like this:
public parseJson(filepath) {
let j = require(filepath);
console.log(j);
}
and PackageB is calling it like this:
let a = new PackageA();
a.parseJson("./file.json");
file.json is in the same directory as PackageB.
CommonJS modules have __dirname variable in their scope, containing a path to directory they reside in.
To get absolute path to RELATIVE_PATH use join(__dirname, RELATIVE_PATH) (join from path module).
example:
// PackageB .js file
const Path = require('path')
const PackageA = require(/* PackageA name or path */)
const PackageB_jsonPathRelative = /* relative path to json file */
// __dirname is directory that contains PackageB .js file
const PackageB_jsonPathAbsolute = Path.join(__dirname, PackageB_jsonPathRelative)
PackageA.parseJson(PackageB_jsonPathAbsolute)
UPDATED
If you can't change PackageB, but you know exactly how PackageA.parseJson is called by PackageB (e.g. directly, or through wrappers, but with known depth), then you can get path to PackageB from stack-trace.
example:
// PackageA .js file
// `npm install stack-trace#0.0.10` if you have `ERR_REQUIRE_ESM` error
const StackTrace = require('stack-trace')
const Path = require('path')
const callerFilename = (skip=0) => StackTrace.get(callerFilename)[skip + 1].getFileName()
module.exports.parseJson = (caller_jsonPathRelative) => {
// we want direct caller of `parseJson` so `skip=0`
// adjust `skip` parameter if caller chain changes
const callerDir = Path.dirname(callerFilename())
// absolute path to json file, from relative to caller file
const jsonPath = Path.join(callerDir, caller_jsonPathRelative)
console.log(jsonPath)
console.log(JSON.parse(require('fs').readFileSync(jsonPath)))
}

No such file or directory when exporting function from another file

src/test.js
module.exports.test = function() {
const { readFileSync } = require('fs');
console.log(readFileSync('test.txt', 'utf8').toString())
}
index.js
const { test } = require('./src/test.js');
test();
Which results in No such file or directory. Does module.exports or exports not work when requiring files in another directory?
When you do something like this:
readFileSync('test.txt', 'utf8')
that attempts to read test.txt from the current working directory. That current working directory is determined by how the main program got started and what the current working directory was when the program was launched. It will have nothing at all to do with the directory your src/test.js module is in.
So, if test.txt is inside the same directory as your src/test.js and you want to read it from there, then you need to manually build a path that references your module's directory. To do that, you can use __dirname which is a special variable set for each module that points to the directory the module is in.
In this case, you can do this:
const path = require('path');
module.exports.test = function() {
const { readFileSync } = require('fs');
console.log(readFileSync(path.join(__dirname, 'test.txt'), 'utf8').toString())
}
And, that will reliably read test.txt from your module's directory.

Node.js serving HTML file doesn't find referenced files

I'm trying to make a simple game using Node.js and HTML. I want to be able to run the game locally (so localhost). But when I start my Node server, only the HTML content is rendered - my images don't show up and the JS is not loaded. I reference JavaScript files, CSS files and images in the HTML code. Not sure why the server cannot reference these from within the HTML.
Here is my server code:
const path = require('path')
const http = require('http')
var fs = require('fs')
//Set port number:
const port = 3000
const requestHandler = (request, response) => {
console.log(request.url)
response.writeHead(200, {"Content-Type": "text/html"});
response.end(fs.readFileSync('game.html'));
}
const server = http.createServer(requestHandler)
server.listen(port, (err) => {
if (err) {
return console.log('Error: ', err);
}
console.log(`Game started\nGo to http://localhost:3000 to play\n`);
console.log(`Server is listening on ${port}...`);
})
Can anyone tell me how I can get my images/JS loaded?
TIA
You are not serving the static content correctly (i.e. images, CSS). Consider what happens when one navigates to http://localhost:3000/some_image.png - they will be served game.html!
You need to account for users requesting that content in your requestHandler by for example reading the file they want with fs.readFileSync(request.url). Remember to check that they can only request files you want them to be able to read!
You need static handlers to return static content. Check this
https://www.pabbly.com/tutorials/node-js-http-module-serving-static-files-html-css-images/.
Also, if your application is going to be huge then use some middle-ware like Express. (https://expressjs.com/)
If you use express framework, you can use static rendering:
app.use(express.static('public'));
where 'public' is the folder name

How to read a symlink in Node.js

I want to read a symlink, and get the details of the link itself, not the contents of the linked file. How do I do that in Node, in a cross-platform way?
I can detect symlinks easily using lstat, no problem. Once I know the path of the file, and that it is a symlink though, how can I read it? fs.readFile always reads the target file, or throws an error for reading a directory for links to directories.
There is a fs.constants.O_SYMLINK constant, which in theory solves this on OSX, but it seems to be undefined on both Ubuntu & Windows 10.
If you have determined that the file is a symlink try this:
fs.readlink("./mysimlink", function (err, linkString) {
// .. do some error handling here ..
console.log(linkString)
});
Confirmed as working on Linux.
You could then use fs.realpath() to turn it into a full path. Be aware though that linkString can be just a filename or relative path as well as a fully qualified path so you may have to get fs.realpath() for the symlink, determine its directory part and prefix it to linkString before using fs.realpath() on it.
I've just faced the same issue: sometimes fs.readlink returns a relative path, sometimes it returns an absolute path.
(proper error handling not implemented to keep things simple)
const fs = require('fs');
const pathPckg = require('path');
async function getTarLinkOfSymLink(path){
return new Promise((resolve, reject)=>{
fs.readlink(path, (err, tarPath)=>{
if(err){
console.log(err.message);
return resolve('');
}
const baseSrcPath = pathPckg.dirname(path);
return resolve( pathPckg.resolve(baseSrcPath, tarPath) );
});
});
}
// usage:
const path = '/example/symbolic/link/path';
const tarPath = await getTarLinkOfSymLink(path);
The code works if the symbolic link is either a file or a directory/folder - tested on Linux

What is the rule for express.static root path?

I ran
node src/app.js
in the mean-to directory
express.static('public')
would work,why?
don't need to specify the path?
what's the rule?
and I know
__dirname+'/../public'`
works just fine.
Just want to make sure the the logic here
I look up the doc
http://expressjs.com/en/starter/static-files.html
it says
"Pass the name of the directory that contains the static assets to the express.static middleware function to start serving the files directly. "
"the path that you provide to the express.static function is relative to the directory from where you launch your node process"
Does that mean
if I run node src/app.js in mean-to folder --> use express.static('public')
if I run node app.js in src folder => use express.static('../public')
and for safety, better use __dirname to get the absolute path of the directory
Express static uses resolve function from module path
Example using path:
var p = require('path');
p.resolve('public'); // will return your absolute path for `public` directory
So rules for express static the same as for path#resolve function
You can see more example in the docs for path module
What's the difference between p.resolve and __dirname?
path#resolve
p.resolve() resolves to an absolute path
path.resolve('public');
Will return your absolute path for public' directory(e.g "C:\projects\myapp\public")
path.resolve('src'); // => "C:\projects\myapp\src")
__dirname
__dirname - name of the directory(absolute path) from which you're currently running your app
file app.js
console.log(__dirname);
Running node app.js will print your absolute path for your app, e.g "C:\projects\myapp"
You mixed 2 lines in one... And both are necessary:
//This line is to say that the statics files are in public
app.use(express.static(__dirname + "/public"));
//This another line is to say that when call the route "/" render a page
app.get("/", function (req, res) {
res.send('Hello World!');
}
Probably, the reason is in running it from directory highter one level than dir with code? Try run it from src:
cd src
node app.js

Categories