3 var path = require('path');
4 var replace = require('replace-ext');
5 var buffer = require('is-buffer');
7 module.exports = VFile;
9 var own = {}.hasOwnProperty;
10 var proto = VFile.prototype;
12 proto.toString = toString;
14 /* Order of setting (least specific to most), we need this because
15 * otherwise `{stem: 'a', path: '~/b.js'}` would throw, as a path
16 * is needed before a stem can be set. */
26 /* Construct a new file. */
27 function VFile(options) {
34 } else if (typeof options === 'string' || buffer(options)) {
35 options = {contents: options};
36 } else if ('message' in options && 'messages' in options) {
40 if (!(this instanceof VFile)) {
41 return new VFile(options);
47 this.cwd = process.cwd();
49 /* Set path related properties in the correct order. */
51 length = order.length;
53 while (++index < length) {
56 if (own.call(options, prop)) {
57 this[prop] = options[prop];
61 /* Set non-path related properties. */
62 for (prop in options) {
63 if (order.indexOf(prop) === -1) {
64 this[prop] = options[prop];
69 /* Access full path (`~/index.min.js`). */
70 Object.defineProperty(proto, 'path', {
72 return this.history[this.history.length - 1];
74 set: function (path) {
75 assertNonEmpty(path, 'path');
77 if (path !== this.path) {
78 this.history.push(path);
83 /* Access parent path (`~`). */
84 Object.defineProperty(proto, 'dirname', {
86 return typeof this.path === 'string' ? path.dirname(this.path) : undefined;
88 set: function (dirname) {
89 assertPath(this.path, 'dirname');
90 this.path = path.join(dirname || '', this.basename);
94 /* Access basename (`index.min.js`). */
95 Object.defineProperty(proto, 'basename', {
97 return typeof this.path === 'string' ? path.basename(this.path) : undefined;
99 set: function (basename) {
100 assertNonEmpty(basename, 'basename');
101 assertPart(basename, 'basename');
102 this.path = path.join(this.dirname || '', basename);
106 /* Access extname (`.js`). */
107 Object.defineProperty(proto, 'extname', {
109 return typeof this.path === 'string' ? path.extname(this.path) : undefined;
111 set: function (extname) {
112 var ext = extname || '';
114 assertPart(ext, 'extname');
115 assertPath(this.path, 'extname');
118 if (ext.charAt(0) !== '.') {
119 throw new Error('`extname` must start with `.`');
122 if (ext.indexOf('.', 1) !== -1) {
123 throw new Error('`extname` cannot contain multiple dots');
127 this.path = replace(this.path, ext);
131 /* Access stem (`index.min`). */
132 Object.defineProperty(proto, 'stem', {
134 return typeof this.path === 'string' ? path.basename(this.path, this.extname) : undefined;
136 set: function (stem) {
137 assertNonEmpty(stem, 'stem');
138 assertPart(stem, 'stem');
139 this.path = path.join(this.dirname || '', stem + (this.extname || ''));
143 /* Get the value of the file. */
144 function toString(encoding) {
145 var value = this.contents || '';
146 return buffer(value) ? value.toString(encoding) : String(value);
149 /* Assert that `part` is not a path (i.e., does
150 * not contain `path.sep`). */
151 function assertPart(part, name) {
152 if (part.indexOf(path.sep) !== -1) {
153 throw new Error('`' + name + '` cannot be a path: did not expect `' + path.sep + '`');
157 /* Assert that `part` is not empty. */
158 function assertNonEmpty(part, name) {
160 throw new Error('`' + name + '` cannot be empty');
164 /* Assert `path` exists. */
165 function assertPath(path, name) {
167 throw new Error('Setting `' + name + '` requires `path` to be set too');