1. Background

The background of writing this article is that today encountered a magic error, let’s take a look

// index.js
console.log(__filename);
// Execute node index.js
// ReferenceError: __filename is not defined in ES module scope
Copy the code

An error was reported when accessing the global variable __filename from node. So I started exploring all the way to get to the root of the problem.

2. Node module mechanism

We know that the early NodeJS module standard used the CommonJS module specification. However, in nodeJS version V13.2.0, it began to support the ES Modules specification. There are several ways to use ES Modules in Node

  • Name the file with the suffix.mjs
  • Package. json added “type”: “module” field

When we use ES Modules in Node, the following global objects and variables will not be available

  • require
  • module.exports
  • exports
  • __filename
  • __dirname
  • NODE_PATH

3. Why use __filename and __dirname with CommonJS modularity?

This problem is mainly attributed to the commonJS nodeJS running mechanism. Many people may think that __filename is the global variable in the Node environment. When this problem occurs, we realize that these two are not the real global variables in node.

Take a look at some simple JS code

(function(){
    console.log(arguments) / / [1, 2, 3]}) (1.2.3)
Copy the code

Arguments can be retrieved from inside the function as arguments passed in when the function is called.

We execute the following code in the Node CommonJS module

// index.js
console.log(arguments);
[Arguments] {
  '0': {},
  '1': [Function: require] {
    resolve: [Function: resolve] { paths: [Function: paths] },
    main: Module {
      id: '. '.path: 'E:\\nodeProjectStorehouse\\nodeStudyFromBook'.exports: {},
      filename: 'E:\\nodeProjectStorehouse\\nodeStudyFromBook\\cc.cjs'.loaded: false.children: [].paths: [Array]},extensions: [Object: null prototype] {
      '.js': [Function (anonymous)],
      '.json': [Function (anonymous)],
      '.node': [Function (anonymous)]
    },
    cache: [Object: null prototype] {
      'E:\\nodeProjectStorehouse\\nodeStudyFromBook\\cc.cjs': [Module]
    }
  },
  '2': Module {
    id: '. '.path: 'E:\\nodeProjectStorehouse\\nodeStudyFromBook'.exports: {},
    filename: 'E:\\nodeProjectStorehouse\\nodeStudyFromBook\\cc.cjs'.loaded: false.children: [].paths: [
      'E:\\nodeProjectStorehouse\\nodeStudyFromBook\\node_modules'.'E:\\nodeProjectStorehouse\\node_modules'.'E:\\node_modules']},'3': 'E:\\nodeProjectStorehouse\\nodeStudyFromBook\\cc.cjs'.'4': 'E:\\nodeProjectStorehouse\\nodeStudyFromBook'
Copy the code

Arguments exports, require, module, __filename, __dirname

This makes it clear that __filename is not a global variable, but a parameter passed in from the outer layer

In that case, let’s go to ES Modules and visit Arguments and see what the result is.

// index.js ES modules
console.log(arguments);
// ReferenceError: arguments is not defined
Copy the code

4. How to use __filename and __dirname in ES Modules?

The node official documentation recommends using import.meta.url in a disguised form

// import.meta.url Returns the absolute 'file:' URL of the module.
// The fileURLToPath() function in the URL module returns the fully parsed path to the platform-specific Node.js file
// The dirname() function in the path module returns the directory path of the path
import { fileURLToPath } from 'url';
import { dirname } from 'path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
Copy the code