Skip to content

Instantly share code, notes, and snippets.

@drodsou
Last active June 5, 2024 15:57
Show Gist options
  • Select an option

  • Save drodsou/de2ba6291aea67ffc5bc4b52d8c32abd to your computer and use it in GitHub Desktop.

Select an option

Save drodsou/de2ba6291aea67ffc5bc4b52d8c32abd to your computer and use it in GitHub Desktop.
Like writeFileSync but creating all folder paths if not exist
// -- updated in 2020/04/19 covering the issues in the comments to this point
// -- remember you also have things like `ensureDirSync` from https://github.com/jprichardson/node-fs-extra/blob/master/docs/ensureDir-sync.md
const fs = require('fs')
function writeFileSyncRecursive(filename, content, charset) {
// -- normalize path separator to '/' instead of path.sep,
// -- as / works in node for Windows as well, and mixed \\ and / can appear in the path
let filepath = filename.replace(/\\/g,'/');
// -- preparation to allow absolute paths as well
let root = '';
if (filepath[0] === '/') {
root = '/';
filepath = filepath.slice(1);
}
else if (filepath[1] === ':') {
root = filepath.slice(0,3); // c:\
filepath = filepath.slice(3);
}
// -- create folders all the way down
const folders = filepath.split('/').slice(0, -1); // remove last item, file
folders.reduce(
(acc, folder) => {
const folderPath = acc + folder + '/';
if (!fs.existsSync(folderPath)) {
fs.mkdirSync(folderPath);
}
return folderPath
},
root // first 'acc', important
);
// -- write file
fs.writeFileSync(root + filepath, content, charset);
}
// -- Tests done that work properly:
// -- both in WSL and CMD
// writeFileSyncRecursive('data.json', '{}', 'utf8');
// writeFileSyncRecursive('public/new-user/data.json', '{}', 'utf8');
// writeFileSyncRecursive('public\\new-user\\data.json', '{}', 'utf8');
// writeFileSyncRecursive('./public/new-user/data.json', '{}', 'utf8');
// writeFileSyncRecursive('.\\public\\new-user\\data.json', '{}', 'utf8');
// writeFileSyncRecursive('/tmp/public\\new-user\\data.json', '{}', 'utf8');
// -- only in WSL (in CMD creates a 'C:\c' folder)
// writeFileSyncRecursive('/c/tmp/writefilesync/public/new-user/data.json', '{}', 'utf8');
// -- only in CMD (in WSL throws error no such file or directory c:/tmp)
// writeFileSyncRecursive('c:\\tmp\\writefilesync\\public\\new-user\\data.json', '{}', 'utf8');
// writeFileSyncRecursive('c:/tmp/writefilesync/public/new-user/data.json', '{}', 'utf8');
/*
// former implementation that received comments until 2020/04/19
// fixed missing second parameter in reduce (irkreja comment)
// works well only with relative paths
// asumes paths passed with / separator even in windows
function writeFileSyncRecursive (filename, content, charset) {
// create folder path if not exists
filename.split('/').slice(0,-1).reduce( (last, folder)=>{
let folderPath = last ? (last + '/' + folder) : folder
if (!fs.existsSync(folderPath)) fs.mkdirSync(folderPath)
return folderPath
},''); // fixed missing second parameter of reduce
fs.writeFileSync(filename, content, charset)
}
*/
@StJohn3D

Copy link
Copy Markdown

Thanks! Works great, saved me some time :)

@shig2k1

shig2k1 commented Dec 17, 2018

Copy link
Copy Markdown

top banana!

@visualjerk

visualjerk commented Jun 14, 2019

Copy link
Copy Markdown

Thanks for this! To make this platform agnostic, you could use path.sep, instead of hardcoding the separator:

const path = require('path')
const fs = require('fs')

function writeFileSyncRecursive(filename, content, charset) {
  const folders = filename.split(path.sep).slice(0, -1)
  if (folders.length) {
    // create folder path if it doesn't exist
    folders.reduce((last, folder) => {
      const folderPath = last ? last + path.sep + folder : folder
      if (!fs.existsSync(folderPath)) {
        fs.mkdirSync(folderPath)
      }
      return folderPath
    })
  }
  fs.writeFileSync(filename, content, charset)
}

@ashwanth1109

Copy link
Copy Markdown

Thanks. This worked great :)

@irkreja

irkreja commented Apr 17, 2020

Copy link
Copy Markdown

writeFileSyncRecursive(`public/new-users/data.json`, content);

this thrown error if public directory does not exist.
Error: ENOENT: no such file or directory, mkdir 'public/new-users'

@drodsou

drodsou commented Apr 19, 2020

Copy link
Copy Markdown
Author

Thanks all for the unexpected comments.
Due to this attention I've updated the code to work more reliably in more cases.

@safonoi

safonoi commented Sep 1, 2020

Copy link
Copy Markdown
const fs = require('fs');
const path = require('path');
const _ = require('lodash');
const { promisify } = require('util');

const writeFile = promisify(fs.writeFile);
const mkdir = promisify(fs.mkdir);
const exists = promisify(fs.exists);

async function writeFileRecursive(filename, content, charset = 'UTF-8') {
  const filePathParts = _.split(filename, path.sep);

  if (_.size(filePathParts) > 1) {
    const folderPath = _.join(_.slice(filePathParts, 0, _.size(filePathParts) - 1), path.sep);
    const folderExists = await exists(folderPath);

    if (!folderExists) {
      await mkdir(folderPath, { recursive: true });
    }
  }

  return writeFile(filename, content, charset);
}

@benchesh

benchesh commented Apr 1, 2021

Copy link
Copy Markdown

This is exactly what I was after. Good stuff.

@juanmendes

Copy link
Copy Markdown

I'd consider just calling fs.mkdirSync(argv.outputDirectory, {recursive: true}); first instead (or the async version)

@ctf0

ctf0 commented Apr 19, 2022

Copy link
Copy Markdown

something simpler could be

function writeFileSyncRecursive(filename, content = '') {
    fs.mkdirSync(path.dirname(filename), {recursive: true})
    fs.writeFileSync(filename, content)
}

@Hasankanso

Hasankanso commented Sep 13, 2022

Copy link
Copy Markdown

something simpler could be

function writeFileSyncRecursive(filename, content = '') {
    fs.mkdirSync(path.dirname(filename), {recursive: true})
    fs.writeFileSync(filename, content)
}

this will create the file name as folder, for example if the filename variable is "c:/users/someone/folder/config.js", config.js will be created as folder, but I agree we can start something from mkdir

@aquacash5

Copy link
Copy Markdown

something simpler could be

function writeFileSyncRecursive(filename, content = '') {
    fs.mkdirSync(path.dirname(filename), {recursive: true})
    fs.writeFileSync(filename, content)
}

this will create the file name as folder, for example if the filename variable is "c:/users/someone/folder/config.js", config.js will be created as folder, but I agree we can start something from mkdir

path.dirname strips the file name off of the path before creating the folders. The function works as intended.

ex.

// This returns "c:/users/someone/folder"
path.dirname("c:/users/someone/folder/config.js")

@DalSoft

DalSoft commented Jun 5, 2024

Copy link
Copy Markdown

something simpler could be

function writeFileSyncRecursive(filename, content = '') {
    fs.mkdirSync(path.dirname(filename), {recursive: true})
    fs.writeFileSync(filename, content)
}

this will create the file name as folder, for example if the filename variable is "c:/users/someone/folder/config.js", config.js will be created as folder, but I agree we can start something from mkdir

path.dirname strips the file name off of the path before creating the folders. The function works as intended.

ex.

// This returns "c:/users/someone/folder"
path.dirname("c:/users/someone/folder/config.js")

This is the the only one that works cross platform, and the bonus is it's the smallest in terms of lines of code too πŸ‘πŸ’―

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment