From f78f4eeb0a7ec52a024b9b34f6c9ee5914765597 Mon Sep 17 00:00:00 2001 From: Jon Schlinkert Date: Mon, 12 Aug 2019 09:30:00 -0400 Subject: [PATCH 1/7] refactor --- .editorconfig | 11 +-- .eslintrc.json | 13 +++ .gitattributes | 11 +-- .travis.yml | 7 +- .verb.md | 58 +++++++++--- CHANGELOG.md | 35 +++++++ LICENSE | 4 +- README.md | 194 +++++++++++++++++++++----------------- example.js | 19 ++-- index.js | 248 ++++++++++++++++++++++++++++--------------------- package.json | 20 ++-- test.js | 147 ----------------------------- test/test.js | 162 ++++++++++++++++++++++++++++++++ 13 files changed, 541 insertions(+), 388 deletions(-) create mode 100644 CHANGELOG.md delete mode 100644 test.js create mode 100644 test/test.js diff --git a/.editorconfig b/.editorconfig index 449f0da..beffa30 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,14 +1,11 @@ -# http://editorconfig.org/ root = true [*] -charset = utf-8 -end_of_line = lf -indent_size = 2 indent_style = space -insert_final_newline = true +indent_size = 2 +charset = utf-8 trim_trailing_whitespace = true +insert_final_newline = true -[{**/{actual,fixtures,expected,templates}/**,*.md}] +[*.md] trim_trailing_whitespace = false -insert_final_newline = false diff --git a/.eslintrc.json b/.eslintrc.json index 61e8895..24b8984 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,4 +1,8 @@ { + "extends": [ + "eslint:recommended" + ], + "env": { "browser": false, "es6": true, @@ -6,6 +10,15 @@ "mocha": true }, + "parserOptions":{ + "ecmaVersion": 9, + "sourceType": "module", + "ecmaFeatures": { + "modules": true, + "experimentalObjectRestSpread": true + } + }, + "globals": { "document": false, "navigator": false, diff --git a/.gitattributes b/.gitattributes index 4a3f1d3..2125666 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,10 +1 @@ -# Enforce Unix newlines -* text eol=lf - -# binaries -*.ai binary -*.psd binary -*.jpg binary -*.gif binary -*.png binary -*.jpeg binary \ No newline at end of file +* text=auto \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index e67bc0c..3102b77 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,11 +2,10 @@ sudo: false os: - linux - osx + - windows language: node_js node_js: - node + - '12' + - '10' - '8' - - '7' - - '6' - - '5' - - '4' diff --git a/.verb.md b/.verb.md index 242ea05..b18f82a 100644 --- a/.verb.md +++ b/.verb.md @@ -1,27 +1,63 @@ ## Usage ```js -var writeFile = require('write'); +const write = require('write'); ``` -## API -{%= apidocs("index.js") %} +## Options -## Release history +The following options may be used with any method. + +### options.newline -### v1.0.2 - 2017-07-11 +**Type**: `boolean` -- improved documentation +**Default**: `undefined` -### v1.0.0 - 2017-07-09 +Ensure that contents has a trailing newline before writing it to the file system. + +```js +write.sync('foo.txt', 'some data...', { newline: true }); +``` -**Added** -- [promise support](#promise) +### options.overwrite + +**Type**: `boolean` + +**Default**: `undefined` + +Set to `false` to prevent existing files from being overwritten. See [increment](#optionsincrement) for a less severe alternative. + +```js +write.sync('foo.txt', 'some data...', { overwrite: false }); +``` -**Changed** +### options.increment -- The main export will now return a promise if no callback is passed +**Type**: `boolean` + +**Default**: `undefined` + +Set to `true` to automatically rename files by appending an increment, like `foo (2).txt`, to prevent `foo.txt` from being overwritten. This is useful when writing log files, or other information where the file name is less important than the contents being written. + +```js +write.sync('foo.txt', 'some data...', { increment: true }); +// if "foo.txt" exists, the file will be renamed to "foo (2).txt" +``` + +## API +{%= apidocs("index.js") %} + +## Release history + +See [CHANGELOG.md]. [fs]: https://nodejs.org/api/fs.html + +[writestream]: https://nodejs.org/api/fs.html#fs_class_fs_writestream +[wsoptions]: https://nodejs.org/api/fs.html#fs_fs_createwritestream_path_options +[writefile]: https://nodejs.org/api/fs.html#fs_fs_writefile_file_data_options_callback +[writefilesync]: https://nodejs.org/api/fs.html#fs_fs_writefilesync_file_data_options +[writable]: https://nodejs.org/api/stream.html#stream_class_stream_writable \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..4cd955a --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,35 @@ +# Changelog + +### v2.0.0 - 2019-08-12 + +**Changes** + +- Refactored code +- Use `fs.createWriteStream` in the main function to improve performance. + +**Added** + +- Added `overwrite` option +- Added `increment` option + +See the [README](readme.md) for more details. + +**Removed** + +- Removed support for passing a custom string on `options.newline`. This should be done before passing the contents to `write()` +- The `.promise` method was removed since _the main export returns a promise, making the method unnecessary_. + + +### v1.0.2 - 2017-07-11 + +- improved documentation + +### v1.0.0 - 2017-07-09 + +**Added** + +- promise support + +**Changed** + +- The main export will now return a promise if no callback is passed \ No newline at end of file diff --git a/LICENSE b/LICENSE index 943e71d..9af4a67 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2014-2017, Jon Schlinkert. +Copyright (c) 2014-present, Jon Schlinkert. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. \ No newline at end of file +THE SOFTWARE. diff --git a/README.md b/README.md index e9666c1..13df990 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,12 @@ -# write [![NPM version](https://img.shields.io/npm/v/write.svg?style=flat)](https://www.npmjs.com/package/write) [![NPM monthly downloads](https://img.shields.io/npm/dm/write.svg?style=flat)](https://npmjs.org/package/write) [![NPM total downloads](https://img.shields.io/npm/dt/write.svg?style=flat)](https://npmjs.org/package/write) [![Linux Build Status](https://img.shields.io/travis/jonschlinkert/write.svg?style=flat&label=Travis)](https://travis-ci.org/jonschlinkert/write) +# write [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=W8YFZ425KND68) [![NPM version](https://img.shields.io/npm/v/write.svg?style=flat)](https://www.npmjs.com/package/write) [![NPM monthly downloads](https://img.shields.io/npm/dm/write.svg?style=flat)](https://npmjs.org/package/write) [![NPM total downloads](https://img.shields.io/npm/dt/write.svg?style=flat)](https://npmjs.org/package/write) [![Linux Build Status](https://img.shields.io/travis/jonschlinkert/write.svg?style=flat&label=Travis)](https://travis-ci.org/jonschlinkert/write) > Write data to a file, replacing the file if it already exists and creating any intermediate directories if they don't already exist. Thin wrapper around node's native fs methods. +Please consider following this project's author, [Jon Schlinkert](https://github.com/jonschlinkert), and consider starring the project to show your :heart: and support. + ## Install -Install with [npm](https://www.npmjs.com/): +Install with [npm](https://www.npmjs.com/) (requires [Node.js](https://nodejs.org/en/) >=4): ```sh $ npm install --save write @@ -13,143 +15,151 @@ $ npm install --save write ## Usage ```js -var writeFile = require('write'); +const write = require('write'); ``` -## API +## Options -### [writeFile](index.js#L40) +The following options may be used with any method. -Asynchronously writes data to a file, replacing the file if it already exists and creating any intermediate directories if they don't already exist. Data can be a string or a buffer. Returns a promise if a callback function is not passed. +### options.newline -**Params** +**Type**: `boolean` -* `filepath` **{string|Buffer|integer}**: filepath or file descriptor. -* `data` **{string|Buffer|Uint8Array}**: String to write to disk. -* `options` **{object}**: Options to pass to [fs.writeFile](https://nodejs.org/api/fs.html#fs_fs_writefile_file_data_options_callback) and/or [mkdirp](https://github.com/substack/node-mkdirp). Some extra options can also be passed (see below) -* `callback` **{Function}**: (optional) If no callback is provided, a promise is returned. +**Default**: `undefined` -**Custom options:** +Ensure that contents has a trailing newline before writing it to the file system. -In addition to [fs.writeFile](https://nodejs.org/api/fs.html#fs_fs_writefile_file_data_options_callback) and [mkdirp](https://github.com/substack/node-mkdirp) options, you can also pass some `write` specific options: +```js +write.sync('foo.txt', 'some data...', { newline: true }); +``` -* `ensureNewLine`: force a new line (`\n`) at the end of the file +### options.overwrite -**Example** +**Type**: `boolean` + +**Default**: `undefined` + +Set to `false` to prevent existing files from being overwritten. See [increment](#optionsincrement) for a less severe alternative. ```js -var writeFile = require('write'); -writeFile('foo.txt', 'This is content...', function(err) { - if (err) console.log(err); -}); +write.sync('foo.txt', 'some data...', { overwrite: false }); +``` -// promise -writeFile('foo.txt', 'This is content...') - .then(function() { - // do stuff - }); +### options.increment + +**Type**: `boolean` + +**Default**: `undefined` + +Set to `true` to automatically rename files by appending an increment, like `foo (2).txt`, to prevent `foo.txt` from being overwritten. This is useful when writing log files, or other information where the file name is less important than the contents being written. + +```js +write.sync('foo.txt', 'some data...', { increment: true }); +// if "foo.txt" exists, the file will be renamed to "foo (2).txt" ``` -### [.promise](index.js#L82) +## API + +### [write](index.js#L40) -The promise version of [writeFile](#writefile). Returns a promise. +Asynchronously writes data to a file, replacing the file if it already exists and creating any intermediate directories if they don't already exist. Data can be a string or a buffer. Returns a promise if a callback function is not passed. **Params** -* `filepath` **{string|Buffer|integer}**: filepath or file descriptor. -* `val` **{string|Buffer|Uint8Array}**: String or buffer to write to disk. -* `options` **{object}**: Options to pass to [fs.writeFile](https://nodejs.org/api/fs.html#fs_fs_writefile_file_data_options_callback) and/or [mkdirp](https://github.com/substack/node-mkdirp) -* `returns` **{Promise}** +* `filepath` **{String}**: file path. +* `data` **{String|Buffer|Uint8Array}**: Data to write. +* `options` **{Object}**: Options to pass to [fs.writeFile](https://nodejs.org/api/fs.html#fs_fs_writefile_file_data_options_callback) +* `callback` **{Function}**: (optional) If no callback is provided, a promise is returned. **Example** ```js -var writeFile = require('write'); -writeFile.promise('foo.txt', 'This is content...') - .then(function() { +const write = require('write'); + +// async/await +(async () => { + await write('foo.txt', 'This is content...'); +})(); + +// promise +write('foo.txt', 'This is content...') + .then(() => { // do stuff }); + +// callback +write('foo.txt', 'This is content...', err => { + // do stuff with err +}); ``` -### [.sync](index.js#L120) +### [.sync](index.js#L87) -The synchronous version of [writeFile](#writefile). Returns undefined. +The synchronous version of [write](#write). Returns undefined. **Params** -* `filepath` **{string|Buffer|integer}**: filepath or file descriptor. -* `data` **{string|Buffer|Uint8Array}**: String or buffer to write to disk. -* `options` **{object}**: Options to pass to [fs.writeFileSync](https://nodejs.org/api/fs.html#fs_fs_writefilesync_file_data_options) and/or [mkdirp](https://github.com/substack/node-mkdirp) +* `filepath` **{String}**: file path. +* `data` **{String|Buffer|Uint8Array}**: Data to write. +* `options` **{Object}**: Options to pass to [fs.writeFileSync](https://nodejs.org/api/fs.html#fs_fs_writefilesync_file_data_options) * `returns` **{undefined}** **Example** ```js -var writeFile = require('write'); -writeFile.sync('foo.txt', 'This is content...'); +const write = require('write'); +write.sync('foo.txt', 'This is content...'); ``` -### [.stream](index.js#L151) +### [.stream](index.js#L126) -Uses `fs.createWriteStream` to write data to a file, replacing the file if it already exists and creating any intermediate directories if they don't already exist. Data can be a string or a buffer. Returns a new [WriteStream](https://nodejs.org/api/fs.html#fs_class_fs_writestream) object. +Returns a new [WriteStream](https://nodejs.org/api/fs.html#fs_class_fs_writestream) object. Uses `fs.createWriteStream` to write data to a file, replacing the file if it already exists and creating any intermediate directories if they don't already exist. Data can be a string or a buffer. **Params** -* `filepath` **{string|Buffer|integer}**: filepath or file descriptor. -* `options` **{object}**: Options to pass to [mkdirp](https://github.com/substack/node-mkdirp) and [fs.createWriteStream](https://nodejs.org/api/fs.html#fs_fs_createwritestream_path_options) +* `filepath` **{String}**: file path. +* `options` **{Object}**: Options to pass to [fs.createWriteStream](https://nodejs.org/api/fs.html#fs_fs_createwritestream_path_options) * `returns` **{Stream}**: Returns a new [WriteStream](https://nodejs.org/api/fs.html#fs_class_fs_writestream) object. (See [Writable Stream](https://nodejs.org/api/stream.html#stream_class_stream_writable)). **Example** ```js -var fs = require('fs'); -var writeFile = require('write'); +const fs = require('fs'); +const write = require('write'); fs.createReadStream('README.md') - .pipe(writeFile.stream('a/b/c/other-file.md')) - .on('close', function() { + .pipe(write.stream('a/b/c/other-file.md')) + .on('close', () => { // do stuff }); ``` ## Release history -### v1.0.2 - 2017-07-11 - -* improved documentation - -### v1.0.0 - 2017-07-09 - -**Added** - -* [promise support](#promise) - -**Changed** - -* The main export will now return a promise if no callback is passed +See [CHANGELOG.md]. ## About -### Related projects +
+Contributing -* [delete](https://www.npmjs.com/package/delete): Delete files and folders and any intermediate directories if they exist (sync and async). | [homepage](https://github.com/jonschlinkert/delete "Delete files and folders and any intermediate directories if they exist (sync and async).") -* [read-data](https://www.npmjs.com/package/read-data): Read JSON or YAML files. | [homepage](https://github.com/jonschlinkert/read-data "Read JSON or YAML files.") -* [read-yaml](https://www.npmjs.com/package/read-yaml): Very thin wrapper around js-yaml for directly reading in YAML files. | [homepage](https://github.com/jonschlinkert/read-yaml "Very thin wrapper around js-yaml for directly reading in YAML files.") -* [write-data](https://www.npmjs.com/package/write-data): Write a YAML or JSON file to disk. Automatically detects the format to write based… [more](https://github.com/jonschlinkert/write-data) | [homepage](https://github.com/jonschlinkert/write-data "Write a YAML or JSON file to disk. Automatically detects the format to write based on extension. Or pass `ext` on the options.") -* [write-json](https://www.npmjs.com/package/write-json): Write a JSON file to disk, also creates intermediate directories in the destination path if… [more](https://github.com/jonschlinkert/write-json) | [homepage](https://github.com/jonschlinkert/write-json "Write a JSON file to disk, also creates intermediate directories in the destination path if they don't already exist.") -* [write-yaml](https://www.npmjs.com/package/write-yaml): Write YAML. Converts JSON to YAML writes it to the specified file. | [homepage](https://github.com/jonschlinkert/write-yaml "Write YAML. Converts JSON to YAML writes it to the specified file.") +Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new). -### Contributing +
-Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new). +
+Running Tests -### Contributors +Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command: + +```sh +$ npm install && npm test +``` -| **Commits** | **Contributor** | -| --- | --- | -| 33 | [jonschlinkert](https://github.com/jonschlinkert) | -| 1 | [tunnckoCore](https://github.com/tunnckoCore) | +
-### Building docs +
+Building docs _(This project's readme.md is generated by [verb](https://github.com/verbose/verb-generate-readme), please don't edit the readme directly. Any changes to the readme must be made in the [.verb.md](.verb.md) readme template.)_ @@ -159,26 +169,40 @@ To generate the readme, run the following command: $ npm install -g verbose/verb#dev verb-generate-readme && verb ``` -### Running tests +
-Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command: +### Related projects -```sh -$ npm install && npm test -``` +You might also be interested in these projects: + +* [delete](https://www.npmjs.com/package/delete): Delete files and folders and any intermediate directories if they exist (sync and async). | [homepage](https://github.com/jonschlinkert/delete "Delete files and folders and any intermediate directories if they exist (sync and async).") +* [read-data](https://www.npmjs.com/package/read-data): Read JSON or YAML files. | [homepage](https://github.com/jonschlinkert/read-data "Read JSON or YAML files.") +* [read-yaml](https://www.npmjs.com/package/read-yaml): Very thin wrapper around js-yaml for directly reading in YAML files. | [homepage](https://github.com/jonschlinkert/read-yaml "Very thin wrapper around js-yaml for directly reading in YAML files.") +* [write-data](https://www.npmjs.com/package/write-data): Write a YAML or JSON file to disk. Automatically detects the format to write based… [more](https://github.com/jonschlinkert/write-data) | [homepage](https://github.com/jonschlinkert/write-data "Write a YAML or JSON file to disk. Automatically detects the format to write based on extension. Or pass `ext` on the options.") +* [write-json](https://www.npmjs.com/package/write-json): Write a JSON file to disk, also creates intermediate directories in the destination path if… [more](https://github.com/jonschlinkert/write-json) | [homepage](https://github.com/jonschlinkert/write-json "Write a JSON file to disk, also creates intermediate directories in the destination path if they don't already exist.") +* [write-yaml](https://www.npmjs.com/package/write-yaml): Write YAML. Converts JSON to YAML writes it to the specified file. | [homepage](https://github.com/jonschlinkert/write-yaml "Write YAML. Converts JSON to YAML writes it to the specified file.") + +### Contributors + +| **Commits** | **Contributor** | +| --- | --- | +| 41 | [jonschlinkert](https://github.com/jonschlinkert) | +| 2 | [jpetitcolas](https://github.com/jpetitcolas) | +| 1 | [tunnckoCore](https://github.com/tunnckoCore) | ### Author **Jon Schlinkert** -* [github/jonschlinkert](https://github.com/jonschlinkert) -* [twitter/jonschlinkert](https://twitter.com/jonschlinkert) +* [GitHub Profile](https://github.com/jonschlinkert) +* [Twitter Profile](https://twitter.com/jonschlinkert) +* [LinkedIn Profile](https://linkedin.com/in/jonschlinkert) ### License -Copyright © 2017, [Jon Schlinkert](https://github.com/jonschlinkert). +Copyright © 2019, [Jon Schlinkert](https://github.com/jonschlinkert). Released under the [MIT License](LICENSE). *** -_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.6.0, on July 11, 2017._ +_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.8.0, on August 12, 2019._ \ No newline at end of file diff --git a/example.js b/example.js index ecfde4c..6e055b0 100644 --- a/example.js +++ b/example.js @@ -1,16 +1,15 @@ +'use strict'; -var writeFile = require('./'); -var Readable = require('stream').Readable; +const { Readable } = require('stream'); +const write = require('./'); -function toStream(str) { - var stream = new Readable(); - stream.push(str); +const toStream = data => { + const stream = new Readable(); + stream.push(data); stream.push(null); return stream; -} +}; toStream('fooo') - .pipe(writeFile.stream('tmp/a/b/c/foo.md')) - .on('close', function() { - console.log('done'); - }) + .pipe(write.stream('tmp/a/b/c/foo.md')) + .on('close', () => console.log('done')); diff --git a/index.js b/index.js index 20d6746..1581e24 100644 --- a/index.js +++ b/index.js @@ -1,15 +1,7 @@ -/*! - * write - * - * Copyright (c) 2014-2017, Jon Schlinkert. - * Released under the MIT License. - */ - 'use strict'; -var fs = require('fs'); -var path = require('path'); -var mkdirp = require('mkdirp'); +const fs = require('fs'); +const path = require('path'); /** * Asynchronously writes data to a file, replacing the file if it already @@ -18,149 +10,197 @@ var mkdirp = require('mkdirp'); * function is not passed. * * ```js - * var writeFile = require('write'); - * writeFile('foo.txt', 'This is content...', function(err) { - * if (err) console.log(err); - * }); + * const write = require('write'); + * + * // async/await + * (async () => { + * await write('foo.txt', 'This is content...'); + * })(); * * // promise - * writeFile('foo.txt', 'This is content...') - * .then(function() { + * write('foo.txt', 'This is content...') + * .then(() => { * // do stuff * }); + * + * // callback + * write('foo.txt', 'This is content...', err => { + * // do stuff with err + * }); * ``` - * @name writeFile - * @param {string|Buffer|integer} `filepath` filepath or file descriptor. - * @param {string|Buffer|Uint8Array} `data` String to write to disk. - * @param {object} `options` Options to pass to [fs.writeFile][fs]{#fs_fs_writefile_file_data_options_callback} and/or [mkdirp][] + * @name write + * @param {String} `filepath` file path. + * @param {String|Buffer|Uint8Array} `data` Data to write. + * @param {Object} `options` Options to pass to [fs.writeFile][writefile] * @param {Function} `callback` (optional) If no callback is provided, a promise is returned. * @api public */ -function writeFile(filepath, data, options, cb) { +const write = (filepath, data, options, callback) => { if (typeof options === 'function') { - cb = options; + callback = options; options = {}; } - if (typeof cb !== 'function') { - return writeFile.promise.apply(null, arguments); - } + const opts = { encoding: 'utf8', ...options }; + const destpath = opts.increment ? incrementName(filepath) : filepath; + const result = { path: destpath, data }; - if (typeof filepath !== 'string') { - cb(new TypeError('expected filepath to be a string')); - return; + if (opts.overwrite === false && exists(filepath, destpath)) { + throw new Error('File already exists: ' + destpath); } - mkdirp(path.dirname(filepath), options, function(err) { - if (err) { - cb(err); - return; - } + const promise = mkdir(path.dirname(destpath), { recursive: true, ...options }) + .then(() => { + return new Promise((resolve, reject) => { + fs.createWriteStream(destpath, opts) + .on('error', err => reject(err)) + .on('close', resolve) + .end(ensureNewline(data, opts)); + }); + }); - var preparedData = data; - if (options && options.ensureNewLine && data.slice(-1) !== "\n") { - preparedData += "\n"; - } + if (typeof callback === 'function') { + promise.then(() => callback(null, result)).catch(callback); + return; + } - fs.writeFile(filepath, preparedData, options, cb); - }); + return promise.then(() => result); }; /** - * The promise version of [writeFile](#writefile). Returns a promise. + * The synchronous version of [write](#write). Returns undefined. * * ```js - * var writeFile = require('write'); - * writeFile.promise('foo.txt', 'This is content...') - * .then(function() { - * // do stuff - * }); + * const write = require('write'); + * write.sync('foo.txt', 'This is content...'); * ``` - * @name .promise - * @param {string|Buffer|integer} `filepath` filepath or file descriptor. - * @param {string|Buffer|Uint8Array} `val` String or buffer to write to disk. - * @param {object} `options` Options to pass to [fs.writeFile][fs]{#fs_fs_writefile_file_data_options_callback} and/or [mkdirp][] - * @return {Promise} + * @name .sync + * @param {String} `filepath` file path. + * @param {String|Buffer|Uint8Array} `data` Data to write. + * @param {Object} `options` Options to pass to [fs.writeFileSync][writefilesync] + * @return {undefined} * @api public */ -writeFile.promise = function(filepath, val, options) { +write.sync = (filepath, data, options) => { if (typeof filepath !== 'string') { - return Promise.reject(new TypeError('expected filepath to be a string')); + throw new TypeError('expected filepath to be a string'); } - return new Promise(function(resolve, reject) { - mkdirp(path.dirname(filepath), options, function(err) { - if (err) { - reject(err); - return; - } - - fs.writeFile(filepath, val, options, function(err) { - if (err) { - reject(err); - return; - } - resolve(val); - }); - }); - }); + const opts = { encoding: 'utf8', ...options }; + const destpath = opts.increment ? incrementName(filepath) : filepath; + + if (opts.overwrite === false && exists(filepath, destpath)) { + throw new Error('File already exists: ' + destpath); + } + + mkdirSync(path.dirname(destpath), { recursive: true, ...options }); + fs.writeFileSync(destpath, ensureNewline(data, opts), opts); + return { path: destpath, data }; }; /** - * The synchronous version of [writeFile](#writefile). Returns undefined. + * Returns a new [WriteStream][writestream] object. Uses `fs.createWriteStream` + * to write data to a file, replacing the file if it already exists and creating + * any intermediate directories if they don't already exist. Data can be a string + * or a buffer. * * ```js - * var writeFile = require('write'); - * writeFile.sync('foo.txt', 'This is content...'); + * const fs = require('fs'); + * const write = require('write'); + * fs.createReadStream('README.md') + * .pipe(write.stream('a/b/c/other-file.md')) + * .on('close', () => { + * // do stuff + * }); * ``` - * @name .sync - * @param {string|Buffer|integer} `filepath` filepath or file descriptor. - * @param {string|Buffer|Uint8Array} `data` String or buffer to write to disk. - * @param {object} `options` Options to pass to [fs.writeFileSync][fs]{#fs_fs_writefilesync_file_data_options} and/or [mkdirp][] - * @return {undefined} + * @name .stream + * @param {String} `filepath` file path. + * @param {Object} `options` Options to pass to [fs.createWriteStream][wsoptions] + * @return {Stream} Returns a new [WriteStream][writestream] object. (See [Writable Stream][writable]). * @api public */ -writeFile.sync = function(filepath, data, options) { +write.stream = (filepath, contents, options) => { if (typeof filepath !== 'string') { throw new TypeError('expected filepath to be a string'); } - mkdirp.sync(path.dirname(filepath), options); - fs.writeFileSync(filepath, data, options); + + const opts = { encoding: 'utf8', ...options }; + const destpath = opts.increment ? incrementName(filepath) : filepath; + + if (opts.overwrite === false && exists(filepath, destpath)) { + throw new Error('File already exists: ' + filepath); + } + + mkdirSync(path.dirname(destpath), { recursive: true, ...options }); + return fs.createWriteStream(destpath, opts); }; /** - * Uses `fs.createWriteStream` to write data to a file, replacing the - * file if it already exists and creating any intermediate directories - * if they don't already exist. Data can be a string or a buffer. Returns - * a new [WriteStream](https://nodejs.org/api/fs.html#fs_class_fs_writestream) - * object. - * - * ```js - * var fs = require('fs'); - * var writeFile = require('write'); - * fs.createReadStream('README.md') - * .pipe(writeFile.stream('a/b/c/other-file.md')) - * .on('close', function() { - * // do stuff - * }); - * ``` - * @name .stream - * @param {string|Buffer|integer} `filepath` filepath or file descriptor. - * @param {object} `options` Options to pass to [mkdirp][] and [fs.createWriteStream][fs]{#fs_fs_createwritestream_path_options} - * @return {Stream} Returns a new [WriteStream](https://nodejs.org/api/fs.html#fs_class_fs_writestream) object. (See [Writable Stream](https://nodejs.org/api/stream.html#stream_class_stream_writable)). - * @api public + * Increment the filename if the file already exists and enabled by the user */ -writeFile.stream = function(filepath, options) { - mkdirp.sync(path.dirname(filepath), options); - return fs.createWriteStream(filepath, options); +const incrementName = destpath => { + let file = { ...path.parse(destpath), path: destpath }; + let name = file.name; + let n = 1; + + while (fs.existsSync(file.path)) { + file.path = path.join(file.dir, `${name} (${++n})${file.ext}`); + } + + return file.path; +}; + +/** + * Ensure newline at EOF if defined on options + */ + +const ensureNewline = (data, options) => { + if (!options || options.newline !== true) return data; + if (typeof data !== 'string' && !isBuffer(data)) { + return data; + } + + // only call `.toString()` on the last character. This way, if + // data is a buffer, we only need to call `.toString()` on + // the entire string if the condition is true. + if (String(data.slice(-1)) !== '\n') { + return data.toString() + '\n'; + } + + return data; +}; + +// if filepath !== destpath, that means the user has enabled +// "increment", which has already checked the file system and +// renamed the file to avoid conflicts, so we don't need to +// check again. +const exists = (filepath, destpath) => { + return filepath === destpath && fs.existsSync(filepath); +}; + +const mkdir = (dirname, options) => { + return new Promise(res => fs.mkdir(dirname, options, () => res())); +}; + +const mkdirSync = (dirname, options) => { + try { + fs.mkdirSync(dirname, options); + } catch (err) { /* do nothing */ } +}; + +const isBuffer = data => { + if (data.constructor && typeof data.constructor.isBuffer === 'function') { + return data.constructor.isBuffer(data); + } + return false; }; /** - * Expose `writeFile` + * Expose `write` */ -module.exports = writeFile; +module.exports = write; diff --git a/package.json b/package.json index 632c788..9d5eed4 100644 --- a/package.json +++ b/package.json @@ -18,21 +18,20 @@ ], "main": "index.js", "engines": { - "node": ">=4" + "node": ">=8" }, "scripts": { "test": "mocha" }, - "dependencies": { - "mkdirp": "^0.5.1" - }, "devDependencies": { - "async-each": "^1.0.1", - "delete": "^1.1.0", - "gulp-format-md": "^1.0.0", - "mocha": "^3.4.2" + "gulp-format-md": "^2.0.0", + "mocha": "^6.2.0", + "rimraf": "^2.6.3" }, "keywords": [ + "async", + "file path", + "file system", "file", "filepath", "files", @@ -42,6 +41,11 @@ "fs.writeFile", "fs.writeFileSync", "path", + "promise", + "streams", + "write file", + "write-file", + "writefile", "write" ], "verb": { diff --git a/test.js b/test.js deleted file mode 100644 index fd4a455..0000000 --- a/test.js +++ /dev/null @@ -1,147 +0,0 @@ -/*! - * write - * - * Copyright (c) 2014-2015, Jon Schlinkert. - * Licensed under the MIT License. - */ - -'use strict'; - -require('mocha'); -var fs = require('fs'); -var assert = require('assert'); -var Readable = require('stream').Readable; -var del = require('delete'); -var each = require('async-each'); -var writeFile = require('./'); -var files = ['tmp/a.md', 'tmp/b.md', 'tmp/c.md', 'tmp/d.md', 'tmp/e.md']; - -function toStream(str) { - var stream = new Readable(); - stream.push(str); - stream.push(null); - return stream; -} - -describe('write', function() { - afterEach(function(cb) { - each(files, function(fp, next) { - fs.stat(fp, next); - }, function(err) { - if (err) return cb(err); - del('tmp', cb); - }); - }); - - describe('End New Line', function () { - it('should just write given data by default', function (done) { - each(files, function (fp, next) { - writeFile(fp, 'Hello!', function () { - fs.readFile(fp, function (err, fileContent) { - if (err) { - return next(err); - } - - assert.equal('Hello!', fileContent.toString()); - next(); - }); - }); - }, done); - }); - - describe('With `ensureNewLine` option to true', function () { - it('should add a new line at the end of the file if none', function (done) { - each(files, function (fp, next) { - writeFile(fp, 'Hello!', { ensureNewLine: true }, function () { - fs.readFile(fp, function (err, fileContent) { - if (err) { - return next(err); - } - - assert.equal('Hello!\n', fileContent.toString()); - next(); - }); - }); - }, done); - }); - - it('should not add a new line at the end of the file if there is already one', function (done) { - each(files, function (fp, next) { - writeFile(fp, "Hello!\n", { ensureNewLine: true }, function() { - fs.readFile(fp, function (err, fileContent) { - if (err) { - return next(err); - } - - assert.equal('Hello!\n', fileContent.toString()); - next(); - }); - }); - }, done); - }); - }); - }); - - describe('async', function() { - it('should write files', function(cb) { - each(files, function(fp, next) { - writeFile(fp, 'content...', next); - }, cb); - }); - - it('should return a promise when no callback is given', function(cb) { - each(files, function(fp, next) { - writeFile(fp, 'content...') - .then(function() { - next(); - }) - .catch(function(err) { - next(err); - }); - }, cb); - }); - }); - - describe('promise', function() { - it('should write files using .promise', function(cb) { - each(files, function(fp, next) { - writeFile.promise(fp, 'content...') - .then(function() { - next(); - }) - .catch(function(err) { - next(err); - }); - }, cb); - }); - }); - - describe('sync', function() { - it('should write files using .sync', function() { - files.forEach(function(fp) { - writeFile.sync(fp, ''); - }); - }); - }); - - describe('stream', function() { - it('should write files using .stream', function(cb) { - each(files, function(fp, next) { - toStream('this is content...') - .pipe(writeFile.stream(fp)) - .on('close', next) - }, cb); - }); - - it('should overwrite an existing file', function(cb) { - var fixtures = files.slice().concat(['tmp/e.md', 'tmp/e.md']); - - each(files, function(fp, next) { - toStream('this is content...') - .pipe(writeFile.stream(fp)) - .on('close', next) - }, cb); - }); - }); -}); - diff --git a/test/test.js b/test/test.js new file mode 100644 index 0000000..2ca69b0 --- /dev/null +++ b/test/test.js @@ -0,0 +1,162 @@ +/*! + * write + * + * Copyright (c) 2014-present, Jon Schlinkert. + * Licensed under the MIT License. + */ + +'use strict'; + +require('mocha'); +const { Readable } = require('stream'); +const fs = require('fs'); +const path = require('path'); +const assert = require('assert').strict; +const rimraf = require('rimraf'); +const write = require('..'); +const tmp = path.join.bind(path, __dirname, 'fixtures'); +const files = ['a.md', 'b.md', 'c.md', 'd.md', 'e.md'].map(n => tmp(n)); + +const toStream = str => { + let stream = new Readable(); + stream.push(str); + stream.push(null); + return stream; +}; + +describe('write', () => { + afterEach(cb => { + rimraf(tmp(), { glob: false }, cb); + }); + + describe('async', () => { + it('should write files', async () => { + for (let file of files) { + await write(file, 'content...'); + assert(fs.existsSync(file)); + } + }); + + it('should not overwrite files when specified', async () => { + for (let file of files) { + write.sync(file, 'content...'); + await assert.rejects(async () => { + return write(file, 'content...', { overwrite: false }); + }); + } + }); + + it('should automatically rename files to avoid conflicts', async () => { + for (let file of files) { + write.sync(file, 'content...'); + const result = await write(file, 'content...', { increment: true }); + assert.notEqual(file, result.path); + assert(fs.existsSync(result.path)); + } + }); + + it('should take a callback', cb => { + let fp = tmp('a.txt'); + write(fp, 'content...', err => { + if (err) { + cb(err); + return; + } + assert(fs.existsSync(fp)); + cb(); + }); + }); + + it('should just write given data by default', cb => { + write(tmp('a.txt'), 'Hello!', err => { + if (err) { + cb(err); + return; + } + + fs.readFile(tmp('a.txt'), (err, buf) => { + if (err) { + cb(err); + } + assert.equal('Hello!', buf.toString()); + cb(); + }); + }); + }); + + it('should add a new line at the end of the file if none', async() => { + for (let file of files) { + await write(file, 'Hello!', { newline: true }); + let contents = fs.readFileSync(file, 'utf8'); + assert.equal('Hello!\n', contents.toString()); + } + }); + }); + + describe('sync', () => { + it('should write files using .sync', () => { + files.forEach(file => { + write.sync(file, 'content...'); + assert(fs.existsSync(file)); + }); + }); + + it('should not overwrite existing files when specified', () => { + files.forEach(file => { + write.sync(file, 'content...'); + assert(fs.existsSync(file)); + assert.throws(() => { + write.sync(file, 'content...', { overwrite: false }); + }); + }); + }); + + it('should not overwrite existing files when specified', () => { + files.forEach(file => { + write.sync(file, 'content...'); + assert(fs.existsSync(file)); + const result = write.sync(file, 'content...', { increment: true }); + assert.notEqual(file, result.path); + assert(fs.existsSync(result.path)); + }); + }); + }); + + describe('stream', () => { + it('should write files using .stream', async() => { + const promise = file => { + return new Promise((resolve, reject) => { + toStream('this is content...') + .pipe(write.stream(file)) + .on('close', resolve) + .on('error', reject); + }); + }; + + for (let file of files) { + await promise(file); + let contents = fs.readFileSync(file, 'utf8'); + assert.equal('this is content...', contents.toString()); + } + }); + + it('should overwrite an existing file', async() => { + const fixtures = files.slice().concat(['e.md', 'e.md']).map(n => tmp(n)); + const promise = file => { + return new Promise((resolve, reject) => { + toStream('this is content...') + .pipe(write.stream(file)) + .on('close', resolve) + .on('error', reject); + }); + }; + + for (let file of fixtures) { + await promise(file); + let contents = fs.readFileSync(file, 'utf8'); + assert.equal('this is content...', contents.toString()); + } + }); + }); +}); + From e996f212fc94bc6ae4f338af91aa5972d4c0fea6 Mon Sep 17 00:00:00 2001 From: Jon Schlinkert Date: Mon, 12 Aug 2019 10:55:00 -0400 Subject: [PATCH 2/7] node 10 --- .travis.yml | 1 - README.md | 8 ++++---- package.json | 4 ++-- test/test.js | 2 ++ 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3102b77..ce7ae03 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,4 +8,3 @@ node_js: - node - '12' - '10' - - '8' diff --git a/README.md b/README.md index 13df990..5b04271 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Please consider following this project's author, [Jon Schlinkert](https://github ## Install -Install with [npm](https://www.npmjs.com/) (requires [Node.js](https://nodejs.org/en/) >=4): +Install with [npm](https://www.npmjs.com/) (requires [Node.js](https://nodejs.org/en/) >=10): ```sh $ npm install --save write @@ -61,7 +61,7 @@ write.sync('foo.txt', 'some data...', { increment: true }); ## API -### [write](index.js#L40) +### [write](index.js#L39) Asynchronously writes data to a file, replacing the file if it already exists and creating any intermediate directories if they don't already exist. Data can be a string or a buffer. Returns a promise if a callback function is not passed. @@ -94,7 +94,7 @@ write('foo.txt', 'This is content...', err => { }); ``` -### [.sync](index.js#L87) +### [.sync](index.js#L86) The synchronous version of [write](#write). Returns undefined. @@ -112,7 +112,7 @@ const write = require('write'); write.sync('foo.txt', 'This is content...'); ``` -### [.stream](index.js#L126) +### [.stream](index.js#L125) Returns a new [WriteStream](https://nodejs.org/api/fs.html#fs_class_fs_writestream) object. Uses `fs.createWriteStream` to write data to a file, replacing the file if it already exists and creating any intermediate directories if they don't already exist. Data can be a string or a buffer. diff --git a/package.json b/package.json index 9d5eed4..b66a482 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ ], "main": "index.js", "engines": { - "node": ">=8" + "node": ">=10" }, "scripts": { "test": "mocha" @@ -75,4 +75,4 @@ "reflinks": true } } -} +} \ No newline at end of file diff --git a/test/test.js b/test/test.js index 2ca69b0..df2fdeb 100644 --- a/test/test.js +++ b/test/test.js @@ -135,6 +135,7 @@ describe('write', () => { for (let file of files) { await promise(file); + assert(fs.existsSync(file)); let contents = fs.readFileSync(file, 'utf8'); assert.equal('this is content...', contents.toString()); } @@ -153,6 +154,7 @@ describe('write', () => { for (let file of fixtures) { await promise(file); + assert(fs.existsSync(file)); let contents = fs.readFileSync(file, 'utf8'); assert.equal('this is content...', contents.toString()); } From 6d9338d703cc8db903d70789c94d6bae5940b698 Mon Sep 17 00:00:00 2001 From: Jon Schlinkert Date: Mon, 12 Aug 2019 13:14:46 -0400 Subject: [PATCH 3/7] fix badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5b04271..e8efecf 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# write [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=W8YFZ425KND68) [![NPM version](https://img.shields.io/npm/v/write.svg?style=flat)](https://www.npmjs.com/package/write) [![NPM monthly downloads](https://img.shields.io/npm/dm/write.svg?style=flat)](https://npmjs.org/package/write) [![NPM total downloads](https://img.shields.io/npm/dt/write.svg?style=flat)](https://npmjs.org/package/write) [![Linux Build Status](https://img.shields.io/travis/jonschlinkert/write.svg?style=flat&label=Travis)](https://travis-ci.org/jonschlinkert/write) +# write [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=W8YFZ425KND68) [![NPM version](https://img.shields.io/npm/v/write.svg?style=flat)](https://www.npmjs.com/package/write) [![NPM monthly downloads](https://img.shields.io/npm/dm/write.svg?style=flat)](https://npmjs.org/package/write) [![NPM total downloads](https://img.shields.io/npm/dt/write.svg?style=flat)](https://npmjs.org/package/write) [![Build Status](https://travis-ci.org/jonschlinkert/write.svg?branch=master)](https://travis-ci.org/jonschlinkert/write) > Write data to a file, replacing the file if it already exists and creating any intermediate directories if they don't already exist. Thin wrapper around node's native fs methods. From f378047fc3532dcfa0184856950baeead494d34b Mon Sep 17 00:00:00 2001 From: Jon Schlinkert Date: Tue, 3 Sep 2019 23:55:11 -0400 Subject: [PATCH 4/7] Fixes per @doowb's code review on https://github.com/jonschlinkert/write/pull/8 --- index.js | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/index.js b/index.js index 1581e24..41e7d7e 100644 --- a/index.js +++ b/index.js @@ -33,6 +33,7 @@ const path = require('path'); * @param {String|Buffer|Uint8Array} `data` Data to write. * @param {Object} `options` Options to pass to [fs.writeFile][writefile] * @param {Function} `callback` (optional) If no callback is provided, a promise is returned. + * @returns {Object} Returns an object with the `path` and `contents` of the file that was written to the file system. This is useful for debugging when `options.increment` is used and the path might have been modified. * @api public */ @@ -79,7 +80,7 @@ const write = (filepath, data, options, callback) => { * @param {String} `filepath` file path. * @param {String|Buffer|Uint8Array} `data` Data to write. * @param {Object} `options` Options to pass to [fs.writeFileSync][writefilesync] - * @return {undefined} + * @returns {Object} Returns an object with the `path` and `contents` of the file that was written to the file system. This is useful for debugging when `options.increment` is used and the path might have been modified. * @api public */ @@ -122,7 +123,7 @@ write.sync = (filepath, data, options) => { * @api public */ -write.stream = (filepath, contents, options) => { +write.stream = (filepath, options) => { if (typeof filepath !== 'string') { throw new TypeError('expected filepath to be a string'); } @@ -164,11 +165,14 @@ const ensureNewline = (data, options) => { return data; } - // only call `.toString()` on the last character. This way, if - // data is a buffer, we only need to call `.toString()` on - // the entire string if the condition is true. + // Only call `.toString()` on the last character. This way, + // if data is a buffer, we don't need to stringify the entire + // buffer just to append a newline. if (String(data.slice(-1)) !== '\n') { - return data.toString() + '\n'; + if (typeof data === 'string') { + return data + '\n'; + } + return data.concat(Buffer.from('\n')); } return data; @@ -183,7 +187,7 @@ const exists = (filepath, destpath) => { }; const mkdir = (dirname, options) => { - return new Promise(res => fs.mkdir(dirname, options, () => res())); + return new Promise(resolve => fs.mkdir(dirname, options, () => resolve())); }; const mkdirSync = (dirname, options) => { From 7feaaf6413adacf2e8d501c18ee01feaa49ea239 Mon Sep 17 00:00:00 2001 From: Jon Schlinkert Date: Tue, 3 Sep 2019 23:58:45 -0400 Subject: [PATCH 5/7] add `prev` check to `incrementName()` --- index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 41e7d7e..685cc73 100644 --- a/index.js +++ b/index.js @@ -146,9 +146,11 @@ write.stream = (filepath, options) => { const incrementName = destpath => { let file = { ...path.parse(destpath), path: destpath }; let name = file.name; + let prev; let n = 1; - while (fs.existsSync(file.path)) { + while (prev !== file.path && fs.existsSync(file.path)) { + prev = file.path; file.path = path.join(file.dir, `${name} (${++n})${file.ext}`); } From 0b288ec7fcaba4f23a1b666d2de6285ed0f981e4 Mon Sep 17 00:00:00 2001 From: Jon Schlinkert Date: Wed, 4 Sep 2019 00:08:10 -0400 Subject: [PATCH 6/7] use `.resolve` instead of `.join` update readme --- .verb.md | 2 +- README.md | 15 ++++++++------- index.js | 2 +- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/.verb.md b/.verb.md index b18f82a..f9948f8 100644 --- a/.verb.md +++ b/.verb.md @@ -60,4 +60,4 @@ See [CHANGELOG.md]. [wsoptions]: https://nodejs.org/api/fs.html#fs_fs_createwritestream_path_options [writefile]: https://nodejs.org/api/fs.html#fs_fs_writefile_file_data_options_callback [writefilesync]: https://nodejs.org/api/fs.html#fs_fs_writefilesync_file_data_options -[writable]: https://nodejs.org/api/stream.html#stream_class_stream_writable \ No newline at end of file +[writable]: https://nodejs.org/api/stream.html#stream_class_stream_writable diff --git a/README.md b/README.md index e8efecf..0f83994 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# write [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=W8YFZ425KND68) [![NPM version](https://img.shields.io/npm/v/write.svg?style=flat)](https://www.npmjs.com/package/write) [![NPM monthly downloads](https://img.shields.io/npm/dm/write.svg?style=flat)](https://npmjs.org/package/write) [![NPM total downloads](https://img.shields.io/npm/dt/write.svg?style=flat)](https://npmjs.org/package/write) [![Build Status](https://travis-ci.org/jonschlinkert/write.svg?branch=master)](https://travis-ci.org/jonschlinkert/write) +# write [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=W8YFZ425KND68) [![NPM version](https://img.shields.io/npm/v/write.svg?style=flat)](https://www.npmjs.com/package/write) [![NPM monthly downloads](https://img.shields.io/npm/dm/write.svg?style=flat)](https://npmjs.org/package/write) [![NPM total downloads](https://img.shields.io/npm/dt/write.svg?style=flat)](https://npmjs.org/package/write) [![Build Status](https://travis-ci.org/jonschlinkert/write.svg?branch=2.0)](https://travis-ci.org/jonschlinkert/write) > Write data to a file, replacing the file if it already exists and creating any intermediate directories if they don't already exist. Thin wrapper around node's native fs methods. @@ -61,7 +61,7 @@ write.sync('foo.txt', 'some data...', { increment: true }); ## API -### [write](index.js#L39) +### [write](index.js#L40) Asynchronously writes data to a file, replacing the file if it already exists and creating any intermediate directories if they don't already exist. Data can be a string or a buffer. Returns a promise if a callback function is not passed. @@ -71,6 +71,7 @@ Asynchronously writes data to a file, replacing the file if it already exists an * `data` **{String|Buffer|Uint8Array}**: Data to write. * `options` **{Object}**: Options to pass to [fs.writeFile](https://nodejs.org/api/fs.html#fs_fs_writefile_file_data_options_callback) * `callback` **{Function}**: (optional) If no callback is provided, a promise is returned. +* `returns` **{Object}**: Returns an object with the `path` and `contents` of the file that was written to the file system. This is useful for debugging when `options.increment` is used and the path might have been modified. **Example** @@ -94,7 +95,7 @@ write('foo.txt', 'This is content...', err => { }); ``` -### [.sync](index.js#L86) +### [.sync](index.js#L87) The synchronous version of [write](#write). Returns undefined. @@ -103,7 +104,7 @@ The synchronous version of [write](#write). Returns undefined. * `filepath` **{String}**: file path. * `data` **{String|Buffer|Uint8Array}**: Data to write. * `options` **{Object}**: Options to pass to [fs.writeFileSync](https://nodejs.org/api/fs.html#fs_fs_writefilesync_file_data_options) -* `returns` **{undefined}** +* `returns` **{Object}**: Returns an object with the `path` and `contents` of the file that was written to the file system. This is useful for debugging when `options.increment` is used and the path might have been modified. **Example** @@ -112,7 +113,7 @@ const write = require('write'); write.sync('foo.txt', 'This is content...'); ``` -### [.stream](index.js#L125) +### [.stream](index.js#L126) Returns a new [WriteStream](https://nodejs.org/api/fs.html#fs_class_fs_writestream) object. Uses `fs.createWriteStream` to write data to a file, replacing the file if it already exists and creating any intermediate directories if they don't already exist. Data can be a string or a buffer. @@ -186,7 +187,7 @@ You might also be interested in these projects: | **Commits** | **Contributor** | | --- | --- | -| 41 | [jonschlinkert](https://github.com/jonschlinkert) | +| 42 | [jonschlinkert](https://github.com/jonschlinkert) | | 2 | [jpetitcolas](https://github.com/jpetitcolas) | | 1 | [tunnckoCore](https://github.com/tunnckoCore) | @@ -205,4 +206,4 @@ Released under the [MIT License](LICENSE). *** -_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.8.0, on August 12, 2019._ \ No newline at end of file +_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.8.0, on September 04, 2019._ \ No newline at end of file diff --git a/index.js b/index.js index 685cc73..07c69f8 100644 --- a/index.js +++ b/index.js @@ -151,7 +151,7 @@ const incrementName = destpath => { while (prev !== file.path && fs.existsSync(file.path)) { prev = file.path; - file.path = path.join(file.dir, `${name} (${++n})${file.ext}`); + file.path = path.resolve(file.dir, `${name} (${++n})${file.ext}`); } return file.path; From 601522a588cb7d4abd988c3677bad92b404e5f7e Mon Sep 17 00:00:00 2001 From: Jon Schlinkert Date: Wed, 4 Sep 2019 00:11:03 -0400 Subject: [PATCH 7/7] use `.resolve` in tests --- test/test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test.js b/test/test.js index df2fdeb..aab6af2 100644 --- a/test/test.js +++ b/test/test.js @@ -14,7 +14,7 @@ const path = require('path'); const assert = require('assert').strict; const rimraf = require('rimraf'); const write = require('..'); -const tmp = path.join.bind(path, __dirname, 'fixtures'); +const tmp = path.resolve.bind(path, __dirname, 'fixtures'); const files = ['a.md', 'b.md', 'c.md', 'd.md', 'e.md'].map(n => tmp(n)); const toStream = str => { @@ -142,7 +142,7 @@ describe('write', () => { }); it('should overwrite an existing file', async() => { - const fixtures = files.slice().concat(['e.md', 'e.md']).map(n => tmp(n)); + const fixtures = [...files, 'e.md', 'e.md'].map(n => tmp(n)); const promise = file => { return new Promise((resolve, reject) => { toStream('this is content...')