From 75b7b084fb20a09a6b50da0f5ec6944fa22620a6 Mon Sep 17 00:00:00 2001 From: Lam Wei Li Date: Wed, 2 Mar 2022 23:26:40 +0800 Subject: [PATCH 1/2] RollingFileWriteStream to create directory recursively (backward-compatible for NodeJS < 10.12.0) --- lib/RollingFileWriteStream.js | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/lib/RollingFileWriteStream.js b/lib/RollingFileWriteStream.js index 12b205b..14c2d2e 100644 --- a/lib/RollingFileWriteStream.js +++ b/lib/RollingFileWriteStream.js @@ -238,7 +238,38 @@ class RollingFileWriteStream extends Writable { } _renewWriteStream() { - fs.ensureDirSync(this.fileObject.dir); + const mkdir = (dir) => { + try { + return fs.mkdirSync(dir, {recursive: true}); + } + // backward-compatible fs.mkdirSync for nodejs pre-10.12.0 (without recursive option) + catch (e) { + // recursive creation of parent first + if (e.code === "ENOENT") { + mkdir(path.dirname(dir)); + return mkdir(dir); + } + + // throw error for all except EEXIST and EROFS (read-only filesystem) + if (e.code !== "EEXIST" && e.code !== "EROFS") { + throw e; + } + + // EEXIST: throw if file and not directory + // EROFS : throw if directory not found + else { + try { + if (fs.statSync(dir).isDirectory()) { + return dir; + } + throw e; + } catch (err) { + throw e; + } + } + } + }; + mkdir(this.fileObject.dir); const filePath = this.fileFormatter({ date: this.state.currentDate, index: 0 From b08827659bd58629c6e4993bf4dc1bae9133b200 Mon Sep 17 00:00:00 2001 From: Lam Wei Li Date: Wed, 2 Mar 2022 23:33:25 +0800 Subject: [PATCH 2/2] Added test cases --- package.json | 2 +- test/RollingFileWriteStream-test.js | 141 ++++++++++++++++++++++++++++ 2 files changed, 142 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index ddbf41f..cea95c0 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,7 @@ "mocha": true }, "parserOptions": { - "ecmaVersion": 8 + "ecmaVersion": 2018 }, "extends": "eslint:recommended", "rules": { diff --git a/test/RollingFileWriteStream-test.js b/test/RollingFileWriteStream-test.js index 734007c..bbf2ed7 100644 --- a/test/RollingFileWriteStream-test.js +++ b/test/RollingFileWriteStream-test.js @@ -1838,6 +1838,147 @@ describe("RollingFileWriteStream", () => { }); }); + describe("when multi-dir does not exist (recursive, nodejs >= 10.12.0)", () => { + const testFile = "tmp_/tmpA/tmpB/tmpC/ignored.log"; + let s; + + before(done => { + fs.removeSync("tmp_/tmpA"); + fakeNow = new Date(2012, 8, 12, 10, 37, 11); + s = new RollingFileWriteStream(testFile); + s.write("test", "utf8", done); + }); + + after(done => { + s.end(() => { + fs.removeSync("tmp_/tmpA"); + done(); + }); + }); + + it("should create the dir", () => { + const files = fs.readdirSync(path.dirname(testFile)); + const expectedFileList = ["ignored.log"]; + files.should.containDeep(expectedFileList); + files.length.should.equal(expectedFileList.length); + + fs.readFileSync(testFile) + .toString() + .should.equal("test"); + }); + }); + + describe("when multi-dir does not exist (non-recursive, nodejs < 10.12.0)", () => { + const testFile = "tmp_/tmpA/tmpB/tmpC/ignored.log"; + let s; + + before(done => { + fs.removeSync("tmp_/tmpA"); + fakeNow = new Date(2012, 8, 12, 10, 37, 11); + const RollingFileWriteStream = proxyquire("../lib/RollingFileWriteStream", { + "./now": mockNow, + "fs-extra": { + mkdirSync(dirPath, options) { + return fs.mkdirSync(dirPath, { ...options, ...{ recursive: false } }); + } + } + }); + s = new RollingFileWriteStream(testFile); + s.write("test", "utf8", done); + }); + + after(done => { + s.end(() => { + fs.removeSync("tmp_/tmpA"); + done(); + }); + }); + + it("should create the dir", () => { + const files = fs.readdirSync(path.dirname(testFile)); + const expectedFileList = ["ignored.log"]; + files.should.containDeep(expectedFileList); + files.length.should.equal(expectedFileList.length); + + fs.readFileSync(testFile) + .toString() + .should.equal("test"); + }); + }); + + describe("when multi-dir does not exist (error handling)", () => { + const testFile = "tmp_/tmpA/tmpB/tmpC/ignored.log"; + let s; + + before(done => { + fs.removeSync("tmp_/tmpA"); + fakeNow = new Date(2012, 8, 12, 10, 37, 11); + done(); + }); + + after(done => { + try { + s.end(() => { + fs.removeSync("tmp_/tmpA"); + done(); + }); + } catch (e) { + done(); + } + }); + + it("should throw EPERM error", () => { + const errorEPERM = new Error("EPERM"); + errorEPERM.code = "EPERM"; + (() => { + const RollingFileWriteStream = proxyquire("../lib/RollingFileWriteStream", { + "./now": mockNow, + "fs-extra": { + mkdirSync() { + throw errorEPERM; + } + } + }); + s = new RollingFileWriteStream(testFile); + }).should.throw(errorEPERM); + }); + + const errorEROFS = new Error("EROFS"); + errorEROFS.code = "EROFS"; + + it("should throw EROFS error", () => { + (() => { + const RollingFileWriteStream = proxyquire("../lib/RollingFileWriteStream", { + "./now": mockNow, + "fs-extra": { + mkdirSync() { + throw errorEROFS; + }, + statSync() { + return { isDirectory() { return false; } }; + } + } + }); + s = new RollingFileWriteStream(testFile); + }).should.throw(errorEROFS); + }); + + it("should not throw EROFS error", () => { + (() => { + fs.mkdirSync("tmp_/tmpA/tmpB/tmpC", { recursive: true }); + const RollingFileWriteStream = proxyquire("../lib/RollingFileWriteStream", { + "./now": mockNow, + "fs-extra": { + mkdirSync() { + throw errorEROFS; + } + } + }); + s = new RollingFileWriteStream(testFile); + }).should.not.throw(errorEROFS); + }); + }); + describe("when given just a base filename with no dir", () => { let s; before(done => {