48

I'm developing on windows, but need to know how to convert a windows path (with backslashes \) into a POSIX path with forward slashes (/)?

My goal is to convert C:\repos\vue-t\tests\views\index\home.vue to C:/repos/vue-t/tests/views/index/home.vue

so I can use it in an import on a file I'm writing to the disk

const appImport = `
import Vue from "vue"
import App from '${path}'

function createApp (data) {
    const app = new Vue({
        data,
        render: h => h(App)
    })
    return app
}`

//this string is then written to the disk as a file

I'd prefer not to .replace(/\\/g, '/') the string, and would rather prefer to use a require('path') function.

Dominus Vilicus
  • 903
  • 2
  • 8
  • 12

7 Answers7

100

Given that all the other answers rely on installing (either way too large, or way too small) third party modules: this can also be done as a one-liner for relative paths (which you should be using 99.999% of the time already anyway) using Node's standard library path module, and more specifically, taking advantage of its dedicated path.posix and path.win32 namespaced properties/functions (introduced all the way back in Node v0.11):

import path from "path"; // or in legacy cjs: const path = require("path")

// Split on whatever is "this OS's path separator",
// then explicitly join with posix or windows separators:

const definitelyPosix = somePathString.split(path.sep).join(path.posix.sep);
const definitelyWindows = somePathString.split(path.sep).join(path.win32.sep);

This will convert your path to POSIX (or Windows) format irrespective of whether you're already on a POSIX (or Windows) compliant platform, without needing any kind of external dependency.

Or, if you don't even care about which OS you're in:

import path from "path";

const { platform } = process;
const locale = path[platform === `win32` ? `win32` : `posix`];

...

const localePath = somepath.split(path.sep).join(locale.sep);

And of course, be aware that paths with / in them have always been valid paths in Windows, ever since the very first windows 1.0 days. So there's not a lot of value in turning Posix paths into Windows paths (manually, at least. If you need absolute paths, Posix and Windows differ drastically in how those are exposed, of course, but pretty much any time you need an absolute path, that's a config value and you should be using .env files anyway)

Mike 'Pomax' Kamermans
  • 49,297
  • 16
  • 112
  • 153
  • 1
    Additional note: if you are dealing with a prefacing drive letter, ex. `D:`, you can add the following to the one liner to remove it: `.substring(2)` – Aaron Meese Feb 16 '22 at 01:44
  • 2
    Thanks to windows network shares, that's a dangerous assumption to make, virtually guaranteed baking bugs into your path handling. It is best not made all by sticking with relative locations (which `path` has several helper functions for). If your code needs to know which drive it's running on, you're already writing cross-platform incompatible code, and it's time to ask yourself why you did that. – Mike 'Pomax' Kamermans Feb 16 '22 at 02:17
  • 2
    The code doesn't need to know what drive it is running on, `path.resolve(__dirname, 'packages')` returns `D:/file/path/here` on Windows. For what it's worth, I used the following in my actual project instead: `return posixPath.replace(/^[a-zA-Z]:/, '')` – Aaron Meese Feb 16 '22 at 22:27
  • 1
    Which is why you don't use `resolve`, you use `path.join` if you're composing paths, but that's all you need 99.9% of the time. In your example, for instance, you literally already have the relative path right there =) – Mike 'Pomax' Kamermans Feb 16 '22 at 22:32
  • 1
    While I understand your point and agree with you, I have a much more complicated use case where this was required; this was a simplified example in case anyone else does as well. Thank you for your amazing answer, it was a big help! – Aaron Meese Feb 16 '22 at 22:38
8

Slash converts windows backslash paths to Unix paths

Usage:

const path = require('path');
const slash = require('slash');

const str = path.join('foo', 'bar');

slash(str);
// Unix    => foo/bar
// Windows => foo/bar
Rajika Imal
  • 625
  • 2
  • 7
  • 15
4

There is node package called upath will convert windows path into unix.

upath = require('upath');

or

import * as upath from 'upath';

upath.toUnix(destination_path)
Mohideen bin Mohammed
  • 18,813
  • 10
  • 112
  • 118
2

For those looking for an answer that doesn't depend on Node.js

One Liner (no 3rd party library)

//
// one-liner
//
let convertPath = (windowsPath) => windowsPath.replace(/^\\\\\?\\/,"").replace(/\\/g,'\/').replace(/\/\/+/g,'\/')

//
// usage
//
convertPath("C:\\repos\\vue-t\\tests\\views\\index\\home.vue")
// >>> "C:/repos/vue-t/tests/views/index/home.vue"

//
// multi-liner (commented and compatible with really old javascript versions)
//
function convertPath(windowsPath) {
    // handle the edge-case of Window's long file names
    // See: https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file#short-vs-long-names
    windowsPath = windowsPath.replace(/^\\\\\?\\/,"");

    // convert the separators, valid since both \ and / can't be in a windows filename
    windowsPath = windowsPath.replace(/\\/g,'\/');

    // compress any // or /// to be just /, which is a safe oper under POSIX
    // and prevents accidental errors caused by manually doing path1+path2
    windowsPath = windowsPath.replace(/\/\/+/g,'\/');

    return windowsPath;
};

// dont want the C: to be inluded? here's a one-liner for that too
let convertPath = (windowsPath) => windowsPath.replace(/^\\\\\?\\/,"").replace(/(?:^C:)?\\/g,'\/').replace(/\/\/+/g,'\/')

Normally I import libraries. However, I went and read the source code for both slash and upath. The functions were not particularly up to date, and incredibly small at the time I checked. In fact, this one liner actually handles more cases than the slash library. Not everyone is looking for this kind of solution, but for those that are, here it is. By coincidence this has the fastest runtime of all the current answers.

Jeff Hykin
  • 1,846
  • 16
  • 25
  • 1
    This totally against modularity and efficient software development. Use `~` to pin the version of the library you are sure it works. If you use `import { toUnix } from 'upath';` it only imports the needed function, so there is no runtime cost. – Amin Ya Jul 05 '20 at 05:04
  • 1
    Thats a good point @Amin, pinning the version with `~` is really useful, and should probably be used more frequently. The runtime cost is probably negligible either way, I was referring to the cost of 10 files you're adding to your node_modules. Do note though: `import { aFunc }` is often not zero cost. It requires 1. using a bundler 2. that bundler having tree shaking and 3. the code be be written in a way that supports being tree-shook. The vast majority of the time libraries are better than copy-paste, but this is some tiny unchanging functionality here. – Jeff Hykin Jul 05 '20 at 17:29
  • 1
    Why... would you do it like this, when `path.sep` and `path.posix.sep` exist? This is a one-liner already: `pathstring.split(path.sep).join(path.posix.sep)` and we're done. It's even cross-platform compatible. – Mike 'Pomax' Kamermans Aug 04 '20 at 17:22
  • I actually like your answer. It doesn't handle the very rare edge case of windows long file names, and doesn't work in a browser. Also I needed the C: removed (and I'm not sure why more people wouldn't prefer that). But in general yours answers the original question in a more clean way, and is debateably the most cross platform. – Jeff Hykin Aug 05 '20 at 18:29
  • I think you need to add more comments to this code. – Drumbeg Aug 19 '20 at 12:18
1

Just use default lib as:

const {direname, resolve, basename}= require('path').posix;

or

import {posix} from 'path';
const {direname, resolve, basename}= posix;
  • 7
    Why write the same answer a year after someone else's? But then without the information that _actually_ answer the question? As is, this should have been a comment. – Mike 'Pomax' Kamermans Feb 26 '22 at 18:25
1

I was looking for something similar, but a little more universal especially to include drives in the absolute path. Thus if you work with e.g. git-bash or WSL usually drives are mapped by default as letters from root / (git-bash) or /mnt (WSL). So here is a regex that does the job for me

// For git-bash Windows drives are mounted in the root like /C/ /D/ etc.
const toGitBashPosixPath = (windowsPath) => windowsPath.replace(/^(\w):|\\+/g,'/$1');

console.log(toGitBashPosixPath('c:\\\\\\project\\file.x')); // messy Windows path
console.log(toGitBashPosixPath('c:\\project\\file.x')); // regular Windows path
console.log(toGitBashPosixPath('c:/project/file.x')); // slash path acceptable by Windows
console.log(toGitBashPosixPath('project\\file.x'));// relative Windows path
console.log(toGitBashPosixPath('.\\project\\file.x'));// another relative Windows path

// For WSL Windows drives are mounted by default next to /mnt like /mnt/C/ /mnt/D/ etc.
const toWSLPosixPath = (windowsPath) => windowsPath.replace(/^(\w):|\\+/g,'/$1').replace(/^\//g,'/mnt/');
console.log(toWSLPosixPath('c:\\project\\file.x'))

Hopefully this will help someone.

-2
const winPath = 'C:\\repos\\vue-t\\tests\\views\\index\\home.vue'
const posixPath = winPath.replace(/\\/g, '/').slice(2)
// Now posixPath = '/repos/vue-t/tests/views/index/home.vue'
Mustafa
  • 931
  • 1
  • 13
  • 26