diff --git a/README.md b/README.md index 14db920..d4d2c61 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,18 @@ File.isVinyl(dummy); // true File.isVinyl(notAFile); // false ``` +### isCustomProp +Vinyl checks if a property is not managed internally, such as `sourceMap`. This is than used in `constructor(options)` when setting, and `clone()` when copying properties. + +```js +var File = require('vinyl'); + +File.isCustomProp('sourceMap'); // true +File.isCustomProp('path'); // false -> internal getter/setter +``` + +Read more in [Extending Vinyl](#extending-vinyl). + ### constructor(options) #### options.cwd Type: `String`

Default: `process.cwd()` @@ -61,6 +73,16 @@ File contents. Type: `Buffer, Stream, or null`

Default: `null` +#### options.{custom} +Any other option properties will just be assigned to the new File object. + +```js +var File = require('vinyl'); + +var file = new File({foo: 'bar'}); +file.foo === 'bar'; // true +``` + ### isBuffer() Returns true if file.contents is a Buffer. @@ -203,6 +225,36 @@ console.log(file.extname); // .js console.log(file.path); // /test/file.js ``` +## Extending Vinyl +When extending Vinyl into your own class with extra features, you need to think about a few things. + +When you have your own properties that are managed internally, you need to extend the static `isCustomProp` method to return `false` when one of these properties is queried. + +```js +const File = require('vinyl'); + +const builtInProps = ['foo', '_foo']; + +class SuperFile extends File { + constructor(options) { + super(options); + this._foo = 'example internal read-only value'; + } + + get foo() { + return this._foo; + } + + static isCustomProp(name) { + return super.isCustomProp(name) && builtInProps.indexOf(name) === -1; + } +} +``` + +This makes properties `foo` and `_foo` ignored when cloning, and when passed in options to `constructor(options)` so they don't get assigned to the new object. + +Same goes for `clone()`. If you have your own internal stuff that needs special handling during cloning, you should extend it to do so. + [npm-url]: https://npmjs.org/package/vinyl [npm-image]: https://badge.fury.io/js/vinyl.svg [travis-url]: https://travis-ci.org/gulpjs/vinyl diff --git a/index.js b/index.js index f082a0b..9bf0b4e 100644 --- a/index.js +++ b/index.js @@ -9,7 +9,13 @@ var inspectStream = require('./lib/inspectStream'); var Stream = require('stream'); var replaceExt = require('replace-ext'); +var builtInFields = [ + '_contents', 'contents', 'stat', 'history', 'path', 'base', 'cwd', +]; + function File(file) { + var self = this; + if (!file) { file = {}; } @@ -28,6 +34,13 @@ function File(file) { this.contents = file.contents || null; this._isVinyl = true; + + // Set custom properties + Object.keys(file).forEach(function(key) { + if (self.constructor.isCustomProp(key)) { + self[key] = file[key]; + } + }); } File.prototype.isBuffer = function() { @@ -48,6 +61,8 @@ File.prototype.isDirectory = function() { }; File.prototype.clone = function(opt) { + var self = this; + if (typeof opt === 'boolean') { opt = { deep: opt, @@ -82,14 +97,10 @@ File.prototype.clone = function(opt) { // Clone our custom properties Object.keys(this).forEach(function(key) { - // Ignore built-in fields - if (key === '_contents' || key === 'stat' || - key === 'history' || key === 'path' || - key === 'base' || key === 'cwd') { - return; - } - file[key] = opt.deep ? clone(this[key], true) : this[key]; - }, this); + if (self.constructor.isCustomProp(key)) { + file[key] = opt.deep ? clone(self[key], true) : self[key]; + } + }); return file; }; @@ -141,6 +152,10 @@ File.prototype.inspect = function() { return ''; }; +File.isCustomProp = function(key) { + return builtInFields.indexOf(key) === -1; +}; + File.isVinyl = function(file) { return (file && file._isVinyl === true) || false; }; diff --git a/test/File.js b/test/File.js index 12c7c4f..4d24729 100644 --- a/test/File.js +++ b/test/File.js @@ -110,6 +110,13 @@ describe('File', function() { file.contents.should.equal(val); done(); }); + + it('should set custom properties', function(done) { + var sourceMap = {}; + var file = new File({ sourceMap: sourceMap }); + file.sourceMap.should.equal(sourceMap); + done(); + }); }); describe('isBuffer()', function() {