-
-
Save drodsou/de2ba6291aea67ffc5bc4b52d8c32abd to your computer and use it in GitHub Desktop.
| // -- 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) | |
| } | |
| */ |
top banana!
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)
}Thanks. This worked great :)
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'
Thanks all for the unexpected comments.
Due to this attention I've updated the code to work more reliably in more cases.
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);
}
This is exactly what I was after. Good stuff.
I'd consider just calling fs.mkdirSync(argv.outputDirectory, {recursive: true}); first instead (or the async version)
something simpler could be
function writeFileSyncRecursive(filename, content = '') {
fs.mkdirSync(path.dirname(filename), {recursive: true})
fs.writeFileSync(filename, content)
}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
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")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.dirnamestrips 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 ππ―
Thanks! Works great, saved me some time :)