-
-
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) | |
| } | |
| */ |
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 👍💯
top banana!