From 35bfe32dc88cb04977b9e1f9ab0f946bef7d88f1 Mon Sep 17 00:00:00 2001 From: Stu Salsbury Date: Fri, 18 Apr 2014 21:29:52 -0700 Subject: [PATCH] feat(clone): option to not clone buffer, support custom properties option `{ contents: false }` causes the clone to reference-copy buffer contents. Custom properties that exist in the original file are deep-cloned to the new File. --- README.md | 25 ++++++++++++++++--------- index.js | 33 ++++++++++++++++++++++----------- package.json | 3 ++- test/File.js | 48 +++++++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 85 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index a33e588..29df968 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ ## Information - + @@ -34,35 +34,35 @@ var coffeeFile = new File({ #### options.cwd -Type: `String` +Type: `String` Default: `process.cwd()` #### options.base Used for relative pathing. Typically where a glob starts. -Type: `String` +Type: `String` Default: `options.cwd` #### options.path Full path to the file. -Type: `String` +Type: `String` Default: `null` #### options.stat The result of an fs.stat call. See [fs.Stats](http://nodejs.org/api/fs.html#fs_class_fs_stats) for more information. -Type: `fs.Stats` +Type: `fs.Stats` Default: `null` #### options.contents File contents. -Type: `Buffer, Stream, or null` +Type: `Buffer, Stream, or null` Default: `null` ### isBuffer() @@ -77,9 +77,16 @@ Returns true if file.contents is a Stream. Returns true if file.contents is null. -### clone() +### clone(options) + +Returns a new File object with all attributes cloned. Custom attributes that +may have been added to the File object are deep-cloned (using the Lodash +cloneDeep function) into the new File. + +#### options.contents -Returns a new File object with all attributes cloned. +If file.contents is a Buffer, and options.contents is false, it will +reference-copy the buffer instead of cloning it. ### pipe(stream[, opt]) @@ -120,4 +127,4 @@ console.log(file.relative); // file.coffee [coveralls-url]: https://coveralls.io/r/wearefractal/vinyl [coveralls-image]: https://coveralls.io/repos/wearefractal/vinyl/badge.png [depstat-url]: https://david-dm.org/wearefractal/vinyl -[depstat-image]: https://david-dm.org/wearefractal/vinyl.png \ No newline at end of file +[depstat-image]: https://david-dm.org/wearefractal/vinyl.png diff --git a/index.js b/index.js index 58c3632..438a127 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,7 @@ var path = require('path'); var cloneStats = require('clone-stats'); +var cloneDeep = require('lodash.clonedeep'); var isBuffer = require('./lib/isBuffer'); var isStream = require('./lib/isStream'); @@ -42,17 +43,27 @@ File.prototype.isDirectory = function() { return this.isNull() && this.stat && this.stat.isDirectory(); }; -File.prototype.clone = function() { - var clonedContents = this.isBuffer() ? cloneBuffer(this.contents) : this.contents; - var clonedStat = this.stat ? cloneStats(this.stat) : null; - - return new File({ - cwd: this.cwd, - base: this.base, - path: this.path, - stat: clonedStat, - contents: clonedContents - }); +File.prototype.clone = function(opt) { + if (!opt) opt = {}; + + var clone = new File(); + + Object.keys(this).forEach(function(key) { + if (key !== '_contents' && key !== 'stat' && this.hasOwnProperty(key)) { + clone[key] = cloneDeep(this[key]); + } + }, this); + + if (opt.contents !== false && this.isBuffer()) { + clone.contents = cloneBuffer(this.contents); + } + else { + clone.contents = this.contents; + } + + clone.stat = this.stat ? cloneStats(this.stat) : null; + + return clone; }; File.prototype.pipe = function(stream, opt) { diff --git a/package.json b/package.json index d851cb1..739f378 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,8 @@ "author": "Fractal (http://wearefractal.com/)", "main": "./index.js", "dependencies": { - "clone-stats": "~0.0.1" + "clone-stats": "~0.0.1", + "lodash.clonedeep": "^2.4.1" }, "devDependencies": { "mocha": "~1.17.0", diff --git a/test/File.js b/test/File.js index 1f2a83a..79c9306 100644 --- a/test/File.js +++ b/test/File.js @@ -80,7 +80,7 @@ describe('File', function() { done(); }); }); - + describe('isBuffer()', function() { it('should return true when the contents are a Buffer', function(done) { var val = new Buffer("test"); @@ -250,6 +250,48 @@ describe('File', function() { done(); }); + + it('should copy custom properties', function(done) { + var options = { + cwd: "/", + base: "/test/", + path: "/test/test.coffee", + contents: null + }; + var file = new File(options); + file.customProp = 'a custom property'; + + var file2 = file.clone(); + + file2.should.not.equal(file, 'refs should be different'); + file2.cwd.should.equal(file.cwd); + file2.base.should.equal(file.base); + file2.path.should.equal(file.path); + file2.customProp.should.equal(file.customProp); + + done(); + }); + + it('should allow to reference-copy a Buffer', function(done) { + var options = { + cwd: "/", + base: "/test/", + path: "/test/test.coffee", + contents: new Buffer("test") + }; + var file = new File(options); + + var file2 = file.clone({ contents: false }); + + file2.should.not.equal(file, 'refs should be different'); + file2.cwd.should.equal(file.cwd); + file2.base.should.equal(file.base); + file2.path.should.equal(file.path); + file2.contents.should.equal(file.contents, 'buffer ref should be the same'); + file2.contents.toString('utf8').should.equal(file.contents.toString('utf8')); + + done(); + }); }); describe('pipe()', function() { @@ -382,7 +424,7 @@ describe('File', function() { process.nextTick(done); }); }); - + describe('inspect()', function() { it('should return correct format when no contents and no path', function(done) { var file = new File(); @@ -445,7 +487,7 @@ describe('File', function() { done(); }); }); - + describe('contents get/set', function() { it('should work with Buffer', function(done) { var val = new Buffer("test");
Packagevinyl