Javascript

  • Don’t use URL to construct paths in Node.js

    I’ve been spending some of my time recently making sure WordPress Playground works in Windows.
    While working on Windows fixes I learned a lot about making sure Javascript code works POSIX and Windows when running it in Node.js.

    One thing that I learned is that you shouldn’t use URL to construct paths.

    If you construct a path using new URL(import.meta.url) in Node.js and use pathname to to obtain the path, it will always have a leading slash in front of the path.
    This is ok for POSIX but in Windows it results in invalid paths
    like /C:/Users/.

    What to use instead of URL

    You can use fileURLToPath from the URL package, it will correctly construct both POSIX and Windows paths from file:/// paths.

    Polyfilling __dirname and __filename in Node 20+

    In Node 20 and above you can use import.meta.filename and import.meta.dirname to polyfill __dirname and __filename.

    Example

    import { fileURLToPath } from 'url';
    import path from 'path';
    
    const windowsImportMetaUrl = "file:///C:/Users/me/Downloads/index.js";
    console.log('windowsImportMetaUrl', windowsImportMetaUrl);
    
    let __dirname = new URL('.', windowsImportMetaUrl).pathname;
    let __filename = new URL(windowsImportMetaUrl).pathname;
    console.log('__filename', __filename);
    console.log('__dirname', __dirname);
    
    
    __filename = fileURLToPath(
        windowsImportMetaUrl,
        {
            windows: true,
        }
    );
    __dirname = path.win32.dirname(__filename);
    console.log('__filename', __filename);
    console.log('__dirname', __dirname);
    
    __filename = import.meta.filename;
    __dirname = import.meta.dirname;
    console.log('__filename', __filename);
    console.log('__dirname', __dirname);

    The above code will result in:

    windowsImportMetaUrl file:///C:/Users/me/Downloads/index.js
    
    # new URL output 
    __filename /C:/Users/me/Downloads/index.js
    __dirname /C:/Users/me/Downloads/
    
    
    # fileURLToPath output
    __filename C:\Users\me\Downloads\index.js
    __dirname C:\Users\me\Downloads
    
    
    # import.meta.filename and import.meta.dirname output
    __filename C:\Users\me\Downloads\index.js
    __dirname C:\Users\me\Downloads