New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
AJShippify opened this Issue Nov 7, 2015 · 51 comments

Comments

Projects
None yet
@AJShippify

AJShippify commented Nov 7, 2015

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

This comment has been minimized.

Member

sokra commented Nov 7, 2015

See node option in the documentation.

@AJShippify

This comment has been minimized.

AJShippify commented Nov 7, 2015

Ok, I set the

node: {
 __dirname: true
    }

But now the __dirname is empty, ''

@AJShippify

This comment has been minimized.

AJShippify commented Nov 8, 2015

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

@bebraw bebraw added the question label Nov 14, 2015

@IngwiePhoenix

This comment has been minimized.

Contributor

IngwiePhoenix commented Nov 17, 2015

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

@goodseller

This comment has been minimized.

goodseller commented Feb 4, 2016

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

This comment has been minimized.

fritx commented Feb 20, 2016

Same 👍
How to disable __dirname injection, leaving it as original?

---UPDATE---

For now, I use this hack:

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

This comment has been minimized.

goodseller commented Feb 20, 2016

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

@fritx

This comment has been minimized.

fritx commented Feb 21, 2016

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

@fritx

This comment has been minimized.

fritx commented Feb 21, 2016

Sorry, I found the hack unnecessary.

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

This comment has been minimized.

kellyrmilligan commented Mar 3, 2016

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

@kellyrmilligan

This comment has been minimized.

kellyrmilligan commented Mar 3, 2016

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

This comment has been minimized.

fritx commented Mar 8, 2016

@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

@joegesualdo

This comment has been minimized.

joegesualdo commented Sep 13, 2016

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?

@kellyrmilligan

This comment has been minimized.

kellyrmilligan commented Sep 13, 2016

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

@michaeljota

This comment has been minimized.

michaeljota commented Sep 14, 2016

I'd like to add that using node option

node: {
  __dirname: false
}

will also work if you are targeting electron.

@mmmeff

This comment has been minimized.

mmmeff commented Oct 3, 2016

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

@tswaters

This comment has been minimized.

tswaters commented Nov 11, 2016

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.

@amasad

This comment has been minimized.

amasad commented Dec 12, 2016

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;
        });
     }

jleclanche added a commit to HearthSim/Sunwell that referenced this issue Dec 28, 2016

@bchr02

This comment has been minimized.

bchr02 commented Feb 17, 2017

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

@nfantone

This comment has been minimized.

nfantone commented Mar 26, 2018

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?

@michaeljota

This comment has been minimized.

michaeljota commented Mar 26, 2018

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

@nfantone

This comment has been minimized.

nfantone commented Mar 26, 2018

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

@michaeljota

This comment has been minimized.

michaeljota commented Mar 26, 2018

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.

@nfantone

This comment has been minimized.

nfantone commented Mar 26, 2018

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.

@michaeljota

This comment has been minimized.

michaeljota commented Mar 26, 2018

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.

@Vadorequest

This comment has been minimized.

Vadorequest commented Mar 26, 2018

@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...

@nfantone

This comment has been minimized.

nfantone commented Mar 27, 2018

@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.

@michaeljota

This comment has been minimized.

michaeljota commented Mar 27, 2018

@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.

@nfantone

This comment has been minimized.

nfantone commented Mar 27, 2018

@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.

@michaeljota

This comment has been minimized.

michaeljota commented Mar 27, 2018

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).

@Vadorequest

This comment has been minimized.

Vadorequest commented Mar 27, 2018

@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.

@nfantone

This comment has been minimized.

nfantone commented Mar 27, 2018

@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.

@michaeljota

This comment has been minimized.

michaeljota commented Mar 27, 2018

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.

@michaeljota

This comment has been minimized.

michaeljota commented Mar 27, 2018

@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.

@michaeljota

This comment has been minimized.

michaeljota commented Mar 27, 2018

@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.

@akshitkrnagpal

This comment has been minimized.

akshitkrnagpal commented May 18, 2018

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

@michaeljota

This comment has been minimized.

michaeljota commented May 18, 2018

@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

This comment has been minimized.

jmca commented May 18, 2018

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.

@anna93

This comment has been minimized.

anna93 commented May 25, 2018

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

This comment has been minimized.

ncaq commented May 26, 2018

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

@timiscoding

This comment has been minimized.

timiscoding commented Jun 14, 2018

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

@Jelledb

This comment has been minimized.

Jelledb commented Aug 9, 2018

@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'));
});
@hackhat

This comment has been minimized.

hackhat commented Sep 2, 2018

In webpack 3.8 I just add this code and it works

  node: {
    __dirname: true,
  },
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment