Webpack does have option to control the value of __dirname in bundled code. However, the description of the API (link) is difficult to understand without a concreted example.

From the docs

node.__dirname
string boolean = false
Options:
- true: The dirname of the input file relative to the context option.
- false: The regular Node.js __dirname behavior. The dirname of the output file when run in a Node.js environment.
- 'mock': The fixed value '/'.

Lets see how this option works

➜  dirname pwd
/home/transang/dirname
➜  dirname cat package.json 
{
  "name": "dirname",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "devDependencies": {
    "webpack": "^4.43.0",
    "webpack-cli": "^3.3.11"
  }
}
➜  dirname node --version             
v14.0.0
➜  dirname yarn --version
1.22.4
➜  dirname cat webpack.config.js 
exports.default = () => ({
	entry: './s1/s2/index.js',
	output: {
		path: `${__dirname}/d1/d2/d3/d4`
	},
	target: 'node',
	node: {__dirname: 'mock'}
})
➜  dirname cat s1/s2/index.js 
console.log(__dirname)
➜  dirname yarn webpack --mode=development
yarn run v1.22.4
$ /home/transang/dirname/node_modules/.bin/webpack --mode=development
Hash: d6da97334da34a12f504
Version: webpack 4.43.0
Time: 41ms
Built at: 05/04/2020 11:36:21 AM
  Asset     Size  Chunks             Chunk Names
main.js  3.9 KiB    main  [emitted]  main
Entrypoint main = main.js
[./s1/s2/index.js] 23 bytes {main} [built]
Done in 0.44s.
➜  dirname cd e1                   
➜  e1 pwd
/home/transang/dirname/e1
➜  e1 node ../d1/d2/d3/d4/main.js
/

I made an experiment with a very simple webpack config

exports.default = () => ({
	entry: './s1/s2/index.js',
	output: {
		path: `${__dirname}/d1/d2/d3/d4`
	},
	target: 'node',
	node: {__dirname: 'mock'}
})

The __dirname option is changed to true, false, 'mock' to test their behaviors.
The bundled code is placed in d1/d2/d3/d4.

In s1/s2, there is index.js

console.log(__dirname, require('path').resolve(__dirname))

I made a directory e1 in the root directory, cd to this directory and run the bundled code (with node ../d1/d2/d3/d4/main.js). Then, I cd back to the root directory, and run the bundled code again (with node d1/d2/d3/d4/main.js)


Experiment result

__dirname output (run from e1) output (run from root)
mock / / / /
true s1/s2 /home/transang/dirname/e1/s1/s2 s1/s2 /home/transang/dirname/s1/s2
false /home/transang/dirname/d1/d2/d3/d4 /home/transang/dirname/d1/d2/d3/d4 /home/transang/dirname/d1/d2/d3/d4 /home/transang/dirname/d1/d2/d3/d4

The results of __dirname are same regardless of the value of cwd. However, when resolved to absolute path, __dirname = true has its disadvantage that the result varies on cwd.

My practical solution is to set __dirname as false (webpack default value), determine your target bundle location statically (assume that it is two level from the root dir), and export as a variable to be used in the whole project.

export rootDir = path.resolve(__dirname, '../..') // bundled code is in two level-depth from the root dir