'use strict'; var path = require('path'); var replace = require('replace-ext'); var buffer = require('is-buffer'); module.exports = VFile; var own = {}.hasOwnProperty; var proto = VFile.prototype; proto.toString = toString; /* Order of setting (least specific to most), we need this because * otherwise `{stem: 'a', path: '~/b.js'}` would throw, as a path * is needed before a stem can be set. */ var order = [ 'history', 'path', 'basename', 'stem', 'extname', 'dirname' ]; /* Construct a new file. */ function VFile(options) { var prop; var index; var length; if (!options) { options = {}; } else if (typeof options === 'string' || buffer(options)) { options = {contents: options}; } else if ('message' in options && 'messages' in options) { return options; } if (!(this instanceof VFile)) { return new VFile(options); } this.data = {}; this.messages = []; this.history = []; this.cwd = process.cwd(); /* Set path related properties in the correct order. */ index = -1; length = order.length; while (++index < length) { prop = order[index]; if (own.call(options, prop)) { this[prop] = options[prop]; } } /* Set non-path related properties. */ for (prop in options) { if (order.indexOf(prop) === -1) { this[prop] = options[prop]; } } } /* Access full path (`~/index.min.js`). */ Object.defineProperty(proto, 'path', { get: function () { return this.history[this.history.length - 1]; }, set: function (path) { assertNonEmpty(path, 'path'); if (path !== this.path) { this.history.push(path); } } }); /* Access parent path (`~`). */ Object.defineProperty(proto, 'dirname', { get: function () { return typeof this.path === 'string' ? path.dirname(this.path) : undefined; }, set: function (dirname) { assertPath(this.path, 'dirname'); this.path = path.join(dirname || '', this.basename); } }); /* Access basename (`index.min.js`). */ Object.defineProperty(proto, 'basename', { get: function () { return typeof this.path === 'string' ? path.basename(this.path) : undefined; }, set: function (basename) { assertNonEmpty(basename, 'basename'); assertPart(basename, 'basename'); this.path = path.join(this.dirname || '', basename); } }); /* Access extname (`.js`). */ Object.defineProperty(proto, 'extname', { get: function () { return typeof this.path === 'string' ? path.extname(this.path) : undefined; }, set: function (extname) { var ext = extname || ''; assertPart(ext, 'extname'); assertPath(this.path, 'extname'); if (ext) { if (ext.charAt(0) !== '.') { throw new Error('`extname` must start with `.`'); } if (ext.indexOf('.', 1) !== -1) { throw new Error('`extname` cannot contain multiple dots'); } } this.path = replace(this.path, ext); } }); /* Access stem (`index.min`). */ Object.defineProperty(proto, 'stem', { get: function () { return typeof this.path === 'string' ? path.basename(this.path, this.extname) : undefined; }, set: function (stem) { assertNonEmpty(stem, 'stem'); assertPart(stem, 'stem'); this.path = path.join(this.dirname || '', stem + (this.extname || '')); } }); /* Get the value of the file. */ function toString(encoding) { var value = this.contents || ''; return buffer(value) ? value.toString(encoding) : String(value); } /* Assert that `part` is not a path (i.e., does * not contain `path.sep`). */ function assertPart(part, name) { if (part.indexOf(path.sep) !== -1) { throw new Error('`' + name + '` cannot be a path: did not expect `' + path.sep + '`'); } } /* Assert that `part` is not empty. */ function assertNonEmpty(part, name) { if (!part) { throw new Error('`' + name + '` cannot be empty'); } } /* Assert `path` exists. */ function assertPath(path, name) { if (!path) { throw new Error('Setting `' + name + '` requires `path` to be set too'); } }