From 5d3f53325eb0c744daaf2ed3797675ef61c34fa5 Mon Sep 17 00:00:00 2001 From: Ryan Zimmerman <17342435+RyanZim@users.noreply.github.com> Date: Fri, 31 Jan 2020 16:27:31 -0500 Subject: [PATCH] Improve ensurefile error (#744) * Check createFileSync gives clear error if node in directory tree is a file * Make error code check work in Node.js 6 * Throw ENOTDIR error when dir doesn't exist * Add test for createFile * Throw ENOTDIR error when dir doesn't exist for makeFile async * Make stat call on parent directory instead of first checking exists Co-authored-by: Luke Childs --- lib/ensure/__tests__/create.test.js | 26 +++++++++++++++++++ lib/ensure/file.js | 40 +++++++++++++++++++++-------- 2 files changed, 56 insertions(+), 10 deletions(-) diff --git a/lib/ensure/__tests__/create.test.js b/lib/ensure/__tests__/create.test.js index 6e2df87b..27f8bd6f 100644 --- a/lib/ensure/__tests__/create.test.js +++ b/lib/ensure/__tests__/create.test.js @@ -42,6 +42,18 @@ describe('fs-extra', () => { done() }) }) + + it('should give clear error if node in directory tree is a file', done => { + const existingFile = path.join(TEST_DIR, Math.random() + 'ts-e', Math.random() + '.txt') + fse.mkdirsSync(path.dirname(existingFile)) + fs.writeFileSync(existingFile) + + const file = path.join(existingFile, Math.random() + '.txt') + fse.createFile(file, err => { + assert.strictEqual(err.code, 'ENOTDIR') + done() + }) + }) }) }) @@ -63,6 +75,20 @@ describe('fs-extra', () => { fse.createFileSync(file) assert.strictEqual(fs.readFileSync(file, 'utf8'), 'hello world') }) + + it('should give clear error if node in directory tree is a file', () => { + const existingFile = path.join(TEST_DIR, Math.random() + 'ts-e', Math.random() + '.txt') + fse.mkdirsSync(path.dirname(existingFile)) + fs.writeFileSync(existingFile) + + const file = path.join(existingFile, Math.random() + '.txt') + try { + fse.createFileSync(file) + assert.fail() + } catch (err) { + assert.strictEqual(err.code, 'ENOTDIR') + } + }) }) }) }) diff --git a/lib/ensure/file.js b/lib/ensure/file.js index 67eed301..b8b2495c 100644 --- a/lib/ensure/file.js +++ b/lib/ensure/file.js @@ -4,7 +4,6 @@ const u = require('universalify').fromCallback const path = require('path') const fs = require('graceful-fs') const mkdir = require('../mkdirs') -const pathExists = require('../path-exists').pathExists function createFile (file, callback) { function makeFile () { @@ -17,13 +16,26 @@ function createFile (file, callback) { fs.stat(file, (err, stats) => { // eslint-disable-line handle-callback-err if (!err && stats.isFile()) return callback() const dir = path.dirname(file) - pathExists(dir, (err, dirExists) => { - if (err) return callback(err) - if (dirExists) return makeFile() - mkdir.mkdirs(dir, err => { - if (err) return callback(err) - makeFile() - }) + fs.stat(dir, (err, stats) => { + if (err) { + // if the directory doesn't exist, make it + if (err.code === 'ENOENT') { + return mkdir.mkdirs(dir, err => { + if (err) return callback(err) + makeFile() + }) + } + return callback(err) + } + + if (stats.isDirectory()) makeFile() + else { + // parent is not a directory + // This is just to cause an internal ENOTDIR error to be thrown + fs.readdir(dir, err => { + if (err) return callback(err) + }) + } }) }) } @@ -36,8 +48,16 @@ function createFileSync (file) { if (stats && stats.isFile()) return const dir = path.dirname(file) - if (!fs.existsSync(dir)) { - mkdir.mkdirsSync(dir) + try { + if (!fs.statSync(dir).isDirectory()) { + // parent is not a directory + // This is just to cause an internal ENOTDIR error to be thrown + fs.readdirSync(dir) + } + } catch (err) { + // If the stat call above failed because the directory doesn't exist, create it + if (err && err.code === 'ENOENT') mkdir.mkdirsSync(dir) + else throw err } fs.writeFileSync(file, '')