'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 // 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'] proto.toString = toString // Access full path (`~/index.min.js`). Object.defineProperty(proto, 'path', {get: getPath, set: setPath}) // Access parent path (`~`). Object.defineProperty(proto, 'dirname', {get: getDirname, set: setDirname}) // Access basename (`index.min.js`). Object.defineProperty(proto, 'basename', {get: getBasename, set: setBasename}) // Access extname (`.js`). Object.defineProperty(proto, 'extname', {get: getExtname, set: setExtname}) // Access stem (`index.min`). Object.defineProperty(proto, 'stem', {get: getStem, set: setStem}) // 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] } } } function getPath() { return this.history[this.history.length - 1] } function setPath(path) { assertNonEmpty(path, 'path') if (path !== this.path) { this.history.push(path) } } function getDirname() { return typeof this.path === 'string' ? path.dirname(this.path) : undefined } function setDirname(dirname) { assertPath(this.path, 'dirname') this.path = path.join(dirname || '', this.basename) } function getBasename() { return typeof this.path === 'string' ? path.basename(this.path) : undefined } function setBasename(basename) { assertNonEmpty(basename, 'basename') assertPart(basename, 'basename') this.path = path.join(this.dirname || '', basename) } function getExtname() { return typeof this.path === 'string' ? path.extname(this.path) : undefined } function setExtname(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) } function getStem() { return typeof this.path === 'string' ? path.basename(this.path, this.extname) : undefined } function setStem(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') } }