webpack/webpack

__dirname returns '/' when js file is built with webpack

Closed this issue ยท 68 comments

I have a working express file, which I 'webpack' it but when the bundled file is run the __dirname global changes to '/'. I need the absolute path for certain functions as res.sendFile. Any help?

sokra commented

See node option in the documentation.

Ok, I set the

node: {
 __dirname: true
    }

But now the __dirname is empty, ''

Could you provide a sample code for retrieving the proper directory name?. I'm currently using a module called app-root-path but I'd like to know if there is a webpack-native way of doing it

Typically, __dirname is set to options.output.publicPath. Switching to node, shouldl resolve this.
However, you also need to set target: "node".

I got the same result after trying the above config.
Here is my config:

webpack.config.js

var webpack = require('webpack');
var path = require('path');
var fs = require('fs');

var nodeModules = {};
fs.readdirSync('node_modules')
  .filter(function(x) {
    return ['.bin'].indexOf(x) === -1;
  })
  .forEach(function(mod) {
    nodeModules[mod] = 'commonjs ' + mod;
  });

var exts = ['express'];
module.exports = {
  entry: ['./server/index.js'],
  context: __dirname,
  node: {
    __filename: true,
    __dirname: true
  },
  target: 'node',
  output: {
    path: path.join(__dirname, 'build'),
    filename: '[name].bundle.js',
    chunkFilename: "[id].bundle.js"
  },
  externals: nodeModules,
  plugins: [
    new webpack.IgnorePlugin(/\.(css|less)$/),
    new webpack.BannerPlugin('require("source-map-support").install();',
                             { raw: true, entryOnly: false }),
  ],
  devtool: 'sourcemap',
}

src: ./server/index.js

...
app.use('/static', express.static(path.join(__dirname, '..', 'public')));
...

It works for directly run the source. Am I missing something?

fritx commented

Same ๐Ÿ‘
How to disable __dirname injection, leaving it as original?

---UPDATE---

For now, I use this hack:

plugins: [
  new webpack.DefinePlugin({
    $dirname: '__dirname',
  }),
]

@fritx I cannot find any related to your usage in the document. May I know how does it works?

fritx commented

@goodseller Just replace __dirname with $dirname, and it works.
The $dirname would be equivalent to __dirname, regardless of the webpack injection.

fritx commented

Sorry, I found the hack unnecessary.

// the webpack config just works
target: 'node',
node: {
  __dirname: false,
  __filename: false,
}

wow, that works for me too, the setting seems counterintuitive. wish we had explanations for what true, false mean in each case.

if I have a node module requiring in a module for me, through a config file, how can I have webpack include the files that are being required in for me so that they are in the build directory? basically I have a hapijs app, and am trying out webpack for the node build. Glue module reads in a config, and the __dirname is now the build directory, and the modules are still in src since they are not being required in explicitly. thoughts?

fritx commented

@kellyrmilligan ๐Ÿ˜ต I think you need to make those modules (in trouble) commonjs?

plugins: [
  new webpack.ExternalsPlugin('commonjs', ['a', 'b'])
]

or just make them all commonjs:

// https://github.com/fritx/os-web/blob/dev/task%2Fwebpack.server.js
externals: [
  (ctx, req,  cb) => {
    // if (/^\.?\//.test(req)) return cb()
    if (/^\.\.?\//.test(req)) return cb() // fixed โˆš
    cb(null, `commonjs ${req}`)
  },
],

I have a project (experimental) ๐Ÿ˜† which webpacks both client & server side.
Check this out: https://github.com/fritx/os-web

if I have a node module requiring in a module for me, through a config file, how can I have webpack include the files that are being required in for me so that they are in the build directory? basically I have a hapijs app, and am trying out webpack for the node build. Glue module reads in a config, and the __dirname is now the build directory, and the modules are still in src since they are not being required in explicitly. thoughts?

@kellyrmilligan Have you found a solution?

I ended up abandoning the approach. Kept on running into brick walls

I'd like to add that using node option

node: {
  __dirname: false
}

will also work if you are targeting electron.

Whatever this flag is doing, setting __dirname: truefixed it for me.

Playing with this a bit, assuming I have the following:

./src
  public
    app.js
  server
    app.js

and inside server/app.js I include the following:

app.use(express.static(path.join(__dirname, '../public')))

The following happens based upon what options.node.__dirname is set to:

  • unset - leading slash, /; express will attempt to serve files from the root, /public (d'oh!)
  • true - sets it to the original path of the src filename, in this case src/server
  • false - no injection is performed, global.__dirname is used and it will be options.output.path, effectively.

The unset case seems the most counter-intuitive to me and I have a hard time understanding what the use case of injecting / as __dirname would be.... strange that this is the default.

FWIW to get the actual behavior in node you need to write your own plugin. This is adapted from the NodeStuffPlugin (which handles the configuration discussed here). Just insert the following into your plugins array:

    {
      apply(compiler) {
        function setModuleConstant(expressionName, fn) {
          compiler.parser.plugin('expression ' + expressionName, function() {
            this.state.current.addVariable(expressionName, JSON.stringify(fn(this.state.module)));
            return true;
          });
        }

        setModuleConstant('__filename', function(module) {
          return module.resource;
        });

        setModuleConstant('__dirname', function(module) {
          return module.context;
        });
     }

For everyone's reference, I just posted a similar issue here: #4303

Why though ?

@mmmeff weird. for me it was __dirname: false that fixed it.

would someone rewrite @amasad 's comment. got this warning

Use 
compiler.plugin("compilation", function(compilation, data) {
  data.normalModuleFactory.plugin("parser", function(parser, options) { parser.plugin(/* ... */); });
}); 
instead.```

for those having this issue, it is also good to note that module.filename would be undefined if you bundled your files. In my case it was undefined and I fixed it by simply replacing it with __filename which yields the same outcome.

@mmclau14 Here's an update that avoids the deprecation warning:

class WebpackDirnamePlugin {
  apply(compiler) {
    function setModuleConstant(expression, fn) {
      compiler.plugin('parser', function(parser) {
        parser.plugin(`expression ${ expression }`, function() {
          this.state.current.addVariable(expression, JSON.stringify(fn(this.state.module)))
          return true
        })
      })
    }

    setModuleConstant('__filename', function(module) {
      return module.resource
    })

    setModuleConstant('__dirname', function(module) {
      return module.context
    })
  }
}

It's ESNext syntax so you'll want to run it through Babel or use webpack.config.babel.js.

https://webpack.js.org/configuration/node/
AJShippify commented on Nov 7, 2015 that made the parameter true and it wont fix, I fixed doing the opposite, turning parameter false:

node: {
  __dirname: false
}

Locally

When I set __dirname and __filename to false, I get a wrong absolute path:

/Users/vadorequest/dev/student-loan-simulator-serverless/aws/loan-advisor/.webpack/service/src

When I set them to true, I get a right relative path:

src

I use console.log(__dirname) to display the __dirname.

How to I get a right absolute path? Here is my webpack.config.js

const slsw = require("serverless-webpack");
const nodeExternals = require("webpack-node-externals");

module.exports = {
  entry: slsw.lib.entries,
  target: "node",
  node: {
    __dirname: false,
    __filename: false,
  },
  // Generate sourcemaps for proper error messages
  devtool: 'source-map',
  // Since 'aws-sdk' is not compatible with webpack,
  // we exclude all node dependencies
  externals: [nodeExternals()],
  // Run babel on all .js files and skip those in node_modules
  module: {
    rules: [
      {
        test: /\.js$/,
        loader: "babel-loader",
        include: __dirname,
        exclude: /node_modules/
      }
    ]
  },
};

On AWS

Whether I set __dirname and __filename to false or true, I get the following absolute path:

/var/task/src -> I have no idea if that's right or wrong path, but file loading is failing with {"errorMessage":"Error: ENOENT: no such file or directory, open '/var/task/src/simulator.hbs'"}

ncaq commented

I used webpack 4.
I try there workanoud that node: {__dirname: true}.
However this workaround is not effect system.

I reinstall webpack 3.
And node: {__dirname: true}.
I confirmed that it is effective.

I think that webpack 4 disable to it workaround.

I encountered this issue today on an Electron + React project (working on macOS).

Doing console.log(__dirname); on a React component, I get:

  • Using { target: 'electron-renderer', node: {__dirname: true} } returns relative paths from build root (e.g.: src/components).

  • Using { target: 'electron-renderer', node: {__dirname: false} } returns absolute paths pointing to node_modules/electron/dist (e.g.: /Users/xxx/path/to/project/node_modules/electron/dist/Electron.app/Contents/Resources/myfile.js)

None of those worked for me. And none of those would be the expected __dirname value for a node module. I'm looking for the absolute path to the file I'm calling console.log from.

Is there a solution/workaround for this?

If you are on React, you would need to set target to electron-renderer. Still, I don't recommend using Node variables inside the Electron Renderer. You may want to call a IPC function from Renderer to Main

@michaeljota electron-renderer is what I'm using, yes. Edited my comment above. Makes absolutely no difference to __dirname, though.

You may want to call a IPC function from Renderer to Main

You want to access the root of your project, but Renderer thread does not have that info. Only Main.

Ok, fair enough. But this being an Electron app is beyond the point. Let's move that detail out for a second.

What I'm after is to be able to pass the absolute path of a file under a certain directory on my project as a file:// URI to a React component. What I'm seeing here is that all solutions proposed in this thread either don't work anymore of produce dubious results which I'm not sure they are useful and/or correct.

OK. I get the point, I don't know how to help. You maybe be able to call the IPC method on Electron, and use Electron to return the path you need. But, I don't think that you can do such task on Web only. Maybe you can ask on Stackover flow for something like this. Or you may want to try target to browser and see how that result at the end.

If you need to use modules from electron, using require you can use window.require instead of a simple require.

@nfantone I also did some tests and concluded that it wasn't really reliable. __dirname and __filename are non-properly working features when using webpack.

I kind of fixed my own issue, which was related to get the absolute path when hosting on AWS, but really it feels more like a monkey patch than a reliable solution.

Now, I have

node: {
    __dirname: false,
    __filename: false,
  }

But I doubt very much it'll work on every environments the same way...

@Vadorequest Yes, my experience was similar. Unfortunately setting those two to false makes __dirname point to/path/to/project/node_modules/electron/dist/Electron.app/Contents/Resources/electron.asar/renderer, which is rather useless.

@nfantone Excuse me if I'm wrong, that I guess I am, but if you were developing a React app that runs into the browser, how do you think it would work? What you want to do.

@michaeljota It doesn't run in the browser. It's an Electron app. But regardless of webpack's target, __dirname should conceptually point to the directory name of the current module (as decribed in its docs). That may change at runtime and it's certainly dependent on where/who runs the application. Perhaps it's my understanding of how the feature is supposed to work what's wrong here, but if webpack offers a way to emulate its behaviour, making it return relative paths (plain wrong, IMHO) or absolute paths to a dependency in node_modules seems odd, to say the very least.

What you want to do.

Following my comment above, I'm looking into preloading a JS script in a <webview> tag. The preload attribute requires a file: (or asar:) URI describing an absolute path to the script being preloaded.

If you can't make it work in the browser, then you won't be able to make it work that way.

The browser part from Electron, is just a regular Chrome browser. Electron makes available some global variables for you to use in the window object, but nothing else. So, you may want to take this approach instead. I think Electron injects a version of __dirname but I don't know, and have no idea where it resolves to.

The point is, this is not a Webpack issue I think. You need a runtime data, but all those variables are being assigned with constant values in the build process.

Think about this, you have several files in several folders, but this is when you are developing. At runtime, you just have one file. (probably).

@michaeljota Not always true, I had this issue with a node.js application, and __dirname was useful to load a script using an absolute path. I was using webpack/babel for ES6+, and it's definitely related to webpack in my case.

@michaeljota That is exactly why I expect __dirname to resolve to different paths depending on environment. Hence,

That may change at runtime and it's certainly dependent on where/who runs the application.

So the root path may be different, but my directories from there are the same between builds/development. My script is always under /path/to/electron.app/lib/my-script.js. Of course, /path/to/electron.app varies and is what I'm trying to reference using __dirname.

I just build all the posibles options for this, with the minimum code to work:

// app.js
console.log(__dirname);

And this config

// webpack.config.js
function generateConfig({ target, __dirname }) {
  return {
    entry: './app.js',
    output: {
      filename: `${target}-${__dirname}.js`,
    },
    target,
    node: {
      __dirname,
    },
  };
}

module.exports = [
  { target: 'node', __dirname: true },
  { target: 'node', __dirname: false },
  { target: 'web', __dirname: true },
  { target: 'web', __dirname: false },
  { target: 'webworker', __dirname: true },
  { target: 'webworker', __dirname: false },
  { target: 'electron-main', __dirname: true },
  { target: 'electron-main', __dirname: false },
  { target: 'electron-renderer', __dirname: true },
  { target: 'electron-renderer', __dirname: false },
].map(options => generateConfig(options));
  • When __dirname is set to true, in ALL configurations the variable was replaced with an empty string.
  • When __dirname was set to false, in ALL configurations the variable was NOT replaced.

Conclusion:

This is more like a doc issue, because this is not explained properly, but I don't think Webpack is doing something wrong. This settings only let you to config Webpack if it should or should not replace the variable with the current relative path.

  • If you are targeting any browser like environment, and you need the value of __dirname in run time you need to have a __dirname variable in the global scope.

  • If you are targeting any node like environment, then __dirname will resolve to the value that node provides.

@nfantone In your case, I think that Electron is injecting its own version of __dirname as a constant value. I don't think this will ever resolve to the directory of your current file in the renderer.

However, you can use several approach to do what you want. You are able to require any module installed for Electron, from the renderer in runtime. I think you can require the native path module here, you could try that. Although, I have the need to say this is not a safe approach. You should never require low level APIs from Electron, and you should actually disable that feature. Use under your own risk.

@Vadorequest In your case, you just need node to resolve, and setting __dirname to false help you because this lets node to resolve instead of Webpack trying to resolve it. Again, don't think this is Webpack fault.

@nfantone
Hey, I am facing the exact same issue you were.
Did you find a safe workaround for that?

@akshitkrnagpal please read some others comments, and see if you find some safe workaround for that.

TL;DR: There is not a workaround, as this is the intended behavior.

jmca commented

Regarding behavior with electron, my current dev env uses an express HMR webpack middleware to serve JS files specifically for Renderers. These files directly access node commonJS stuff.

This combination of Webpack config works for me:

node: {
    __dirname: true,
},
  ...
target: 'electron-renderer',

From Webpack docs:

node.__dirname
true: The dirname of the input file relative to the context option.

For "context":

By default the current directory is used, but it's recommended to pass a value in your configuration

For my project the "current directory" is fine because it is my project root.

Why true?
false would always lead to the index.html dir that loads the JS bundle, so that doesn't help.
mock would yield / which is definitely incorrect.

Now take for example a file in PROJECT_ROOT/dirA/dirB/aFile
Using __dirname in aFile will yield dirA/dirB/aFile.

path.resolve() docs say:

If after processing all given path segments an absolute path has not yet been generated, the current working directory is used.

Since__dirname yields a relative path, "the current working directory" in this case is my project root.

path.resolve(__dirname) correctly yields /full/path/to/project/dirA/dirB/aFile.

node: {
  __dirname: false
}

worked for me and not this

node: {
  __dirname: true
}

false not true. I am using webpack4.8.3 and electron2.0.2, this is without webpack-hmr

ncaq commented

I think false is correct.
If you use Devtron, to app copy file.

If you're running server-side code, you shouldn't be using the node option at all. From the Node configuration docs:

These options configure whether to polyfill or mock certain Node.js globals and modules. This allows code originally written for the Node.js environment to run in other environments like the browser.

Then at the bottom of the Node section

Since webpack 3.0.0, the node option may be set to false to completely turn off the NodeStuffPlugin and NodeSourcePlugin plugins.

{
  node: false
}

Doing this will give back the original node behaviour of __dirname and __filename amongst other node variables.

But you will still run into issues if you have multiple build environments that install into different path hierarchies which is why you'd need to write your own plugin as @amasad comments #1599 (comment)

I've updated his plugin to work with latest webpack as of writing

@timiscoding his solution worked, however I'm also using local node modules, and these do not use the correct __dirname. Instead the __dirname is set to the directory that the bundle was built/ran in.

My local node module looks like this: "player-core": "file:../../../core"

When I regularly run my project, console.log(__dirname) prints correctly: C:\Users\jelledb\Documents\my_project\core\backend\dist\views, but when I use the webpack build, it will use the directory where the webpack bundle was built: C:\Users\jelledb\Documents\my_project\players\webos\signageService\webpack-build .

I use this to share a bunch of expressJS routes that all serve a view (.html file), and this core module is shared across multiple projects.

This is the test-code that was used:

router.get('/admin', (req, res) => {
    console.log('/admin dir: ', __dirname);
    res.sendFile(path.join(__dirname, '../../views/admin.html'));
});

In webpack 3.8 I just add this code and it works

  node: {
    __dirname: true,
  },

Setting

  node: false,

worked for me.

This should really be default IMO. It is dangerous to just change the expected __dirname to root directory because there is a risk of accidentally modifying unexpected files.

Do we have a working solution for using __dirname with webpack + electron yet? I have spent so much time trying different things, but cannot seem to get __dirname work at all. Couldn't find anything in electron community as well.

Worse is that __dirname is used by a dependency that I am using in my app and I don't have an option to replace that dependency. Only option is to make __dirname work somehow. It has been really frustrating. Appreciate any and all the help I can get to make it work with electron.
Cheers!

Same here. I've been able to work around it by adding an environment variable tied to app.getAppPath(), but I am still frustrated that I can't understand how the __dirname stuff works with node and electron. Really makes testing tricky between dev and prod environments.

@saurabh-deep

I was running into the issue where __dirname was set to / in my electron main process bundle with webpack. I added

node: {
__dirname: false
}

to my config and that did the trick

My full config ๐Ÿ‘‡

const path = require('path');

module.exports = {
    entry: path.join(__dirname, '..', 'main', 'index.js'),
    output: {
        path: path.resolve(__dirname, '..', 'public'),
        filename: 'electron.js',
    },
    target: 'electron-main',
    node: {
        __dirname: false,
    },
};

I have the same issue but in create-react-app and I don't want to eject the scripts, so don't have access to webpack configs... Any suggestions? getting really frustrated on this one

thanks

Still doesn't understand how use it with create-react-app + electron. In dev mode I set __dirname=true; target = 'electron-renderer' and everything works fine but in a production mode it's crashing. I'm using leveldown lib for my local db and can't make it working in production mode

@Kupstas you might also try process.cwd() (not sure if that would work for you, but it may be worth trying?)

@bvaughn I think that problem in some other place. In dev mode in __dirname I see node_modules/leveldown but in production /Users/nikolay.kupstas/Desktop/Work/bus-inspector/dist/mac/Busman.app/Contents/Resources/app.asar/build.

I am encountering this issue. I will get around it for now by using process.cwd() and avoiding __dirname and __filename in my app, but that's not ideal.

royra commented

Seems like webpack incorrectly replaces __dirname and __filename for sibling modules in a monorepo (using yarn workspaces). __dirname seems to always be set to the root context (the package webpack is run at).

I got around this by applying an updated version of @amasad's custom plugin above:

    {
      apply(compiler) {
        function setModuleConstant(expressionName, fn) {
          compiler.hooks.normalModuleFactory.tap('MyPlugin', factory => {
            factory.hooks.parser.for('javascript/auto').tap('MyPlugin', (parser, _options) => {
              parser.hooks.expression.for(expressionName).tap('MyPlugin', expression => {
                parser.state.current.addVariable(expressionName, JSON.stringify(fn(parser.state.module)))
                return true
              })
            })
          })
        }

        setModuleConstant('__filename', function (module) {
          return module.resource;
        });

        setModuleConstant('__dirname', function (module) {
          return module.context;
        });
      }
    },

If you are using webpack to bundle to target electron-renderer and found some __dirname in node_modules does not output as the real path, probably you need to add this module to webpack external.

Related to this issue, the documentation misleadingly indicates that the default value for both __filename and __dirname is false. However, setting both to false complete changed (fixed) the behaviour for me, meaning the default must be something else.

Fixed in webpack@5, for node, async-node and electron-main, it is false by default

Same ๐Ÿ‘
How to disable __dirname injection, leaving it as original?

---UPDATE---

For now, I use this hack:

plugins: [
  new webpack.DefinePlugin({
    $dirname: '__dirname',
  }),
]

Doesn't work.

@saurabh-deep

I was running into the issue where __dirname was set to / in my electron main process bundle with webpack. I added

node: {
__dirname: false
}

to my config and that did the trick

My full config ๐Ÿ‘‡

const path = require('path');

module.exports = {
    entry: path.join(__dirname, '..', 'main', 'index.js'),
    output: {
        path: path.resolve(__dirname, '..', 'public'),
        filename: 'electron.js',
    },
    target: 'electron-main',
    node: {
        __dirname: false,
    },
};

set __dirname: false worked for me.

vjpr commented

I had issues with this working sporadically. I had to clear my cache sometimes. I also changed the resolve.conditionNames which broke things.

So be warned, __dirname might be getting overridden somewhere else when you tweak other settings. I think some comments hinted at sporadic behavior of this issue.

I searched for where __dirname is assigned and found this which might be causing issue. Something to do with the ESM loader.

node:internal/process/execution

Screen Shot 2021-11-09 at 8 27 06 pm

The custom plugin #1599 (comment) is the best solution.