Initial commit
[yaffs-website] / node_modules / vinyl / index.js
1 var path = require('path');
2 var clone = require('clone');
3 var cloneStats = require('clone-stats');
4 var cloneBuffer = require('./lib/cloneBuffer');
5 var isBuffer = require('./lib/isBuffer');
6 var isStream = require('./lib/isStream');
7 var isNull = require('./lib/isNull');
8 var inspectStream = require('./lib/inspectStream');
9 var Stream = require('stream');
10 var replaceExt = require('replace-ext');
11
12 function File(file) {
13   if (!file) file = {};
14
15   // record path change
16   var history = file.path ? [file.path] : file.history;
17   this.history = history || [];
18
19   this.cwd = file.cwd || process.cwd();
20   this.base = file.base || this.cwd;
21
22   // stat = files stats object
23   this.stat = file.stat || null;
24
25   // contents = stream, buffer, or null if not read
26   this.contents = file.contents || null;
27
28   this._isVinyl = true;
29 }
30
31 File.prototype.isBuffer = function() {
32   return isBuffer(this.contents);
33 };
34
35 File.prototype.isStream = function() {
36   return isStream(this.contents);
37 };
38
39 File.prototype.isNull = function() {
40   return isNull(this.contents);
41 };
42
43 // TODO: should this be moved to vinyl-fs?
44 File.prototype.isDirectory = function() {
45   return this.isNull() && this.stat && this.stat.isDirectory();
46 };
47
48 File.prototype.clone = function(opt) {
49   if (typeof opt === 'boolean') {
50     opt = {
51       deep: opt,
52       contents: true
53     };
54   } else if (!opt) {
55     opt = {
56       deep: true,
57       contents: true
58     };
59   } else {
60     opt.deep = opt.deep === true;
61     opt.contents = opt.contents !== false;
62   }
63
64   // clone our file contents
65   var contents;
66   if (this.isStream()) {
67     contents = this.contents.pipe(new Stream.PassThrough());
68     this.contents = this.contents.pipe(new Stream.PassThrough());
69   } else if (this.isBuffer()) {
70     contents = opt.contents ? cloneBuffer(this.contents) : this.contents;
71   }
72
73   var file = new File({
74     cwd: this.cwd,
75     base: this.base,
76     stat: (this.stat ? cloneStats(this.stat) : null),
77     history: this.history.slice(),
78     contents: contents
79   });
80
81   // clone our custom properties
82   Object.keys(this).forEach(function(key) {
83     // ignore built-in fields
84     if (key === '_contents' || key === 'stat' ||
85       key === 'history' || key === 'path' ||
86       key === 'base' || key === 'cwd') {
87       return;
88     }
89     file[key] = opt.deep ? clone(this[key], true) : this[key];
90   }, this);
91   return file;
92 };
93
94 File.prototype.pipe = function(stream, opt) {
95   if (!opt) opt = {};
96   if (typeof opt.end === 'undefined') opt.end = true;
97
98   if (this.isStream()) {
99     return this.contents.pipe(stream, opt);
100   }
101   if (this.isBuffer()) {
102     if (opt.end) {
103       stream.end(this.contents);
104     } else {
105       stream.write(this.contents);
106     }
107     return stream;
108   }
109
110   // isNull
111   if (opt.end) stream.end();
112   return stream;
113 };
114
115 File.prototype.inspect = function() {
116   var inspect = [];
117
118   // use relative path if possible
119   var filePath = (this.base && this.path) ? this.relative : this.path;
120
121   if (filePath) {
122     inspect.push('"'+filePath+'"');
123   }
124
125   if (this.isBuffer()) {
126     inspect.push(this.contents.inspect());
127   }
128
129   if (this.isStream()) {
130     inspect.push(inspectStream(this.contents));
131   }
132
133   return '<File '+inspect.join(' ')+'>';
134 };
135
136 File.isVinyl = function(file) {
137   return file && file._isVinyl === true;
138 };
139
140 // virtual attributes
141 // or stuff with extra logic
142 Object.defineProperty(File.prototype, 'contents', {
143   get: function() {
144     return this._contents;
145   },
146   set: function(val) {
147     if (!isBuffer(val) && !isStream(val) && !isNull(val)) {
148       throw new Error('File.contents can only be a Buffer, a Stream, or null.');
149     }
150     this._contents = val;
151   }
152 });
153
154 // TODO: should this be moved to vinyl-fs?
155 Object.defineProperty(File.prototype, 'relative', {
156   get: function() {
157     if (!this.base) throw new Error('No base specified! Can not get relative.');
158     if (!this.path) throw new Error('No path specified! Can not get relative.');
159     return path.relative(this.base, this.path);
160   },
161   set: function() {
162     throw new Error('File.relative is generated from the base and path attributes. Do not modify it.');
163   }
164 });
165
166 Object.defineProperty(File.prototype, 'dirname', {
167   get: function() {
168     if (!this.path) throw new Error('No path specified! Can not get dirname.');
169     return path.dirname(this.path);
170   },
171   set: function(dirname) {
172     if (!this.path) throw new Error('No path specified! Can not set dirname.');
173     this.path = path.join(dirname, path.basename(this.path));
174   }
175 });
176
177 Object.defineProperty(File.prototype, 'basename', {
178   get: function() {
179     if (!this.path) throw new Error('No path specified! Can not get basename.');
180     return path.basename(this.path);
181   },
182   set: function(basename) {
183     if (!this.path) throw new Error('No path specified! Can not set basename.');
184     this.path = path.join(path.dirname(this.path), basename);
185   }
186 });
187
188 Object.defineProperty(File.prototype, 'extname', {
189   get: function() {
190     if (!this.path) throw new Error('No path specified! Can not get extname.');
191     return path.extname(this.path);
192   },
193   set: function(extname) {
194     if (!this.path) throw new Error('No path specified! Can not set extname.');
195     this.path = replaceExt(this.path, extname);
196   }
197 });
198
199 Object.defineProperty(File.prototype, 'path', {
200   get: function() {
201     return this.history[this.history.length - 1];
202   },
203   set: function(path) {
204     if (typeof path !== 'string') throw new Error('path should be string');
205
206     // record history only when path changed
207     if (path && path !== this.path) {
208       this.history.push(path);
209     }
210   }
211 });
212
213 module.exports = File;