core.js 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. 'use strict'
  2. var path = require('path')
  3. var replace = require('replace-ext')
  4. var buffer = require('is-buffer')
  5. module.exports = VFile
  6. var own = {}.hasOwnProperty
  7. var proto = VFile.prototype
  8. // Order of setting (least specific to most), we need this because otherwise
  9. // `{stem: 'a', path: '~/b.js'}` would throw, as a path is needed before a
  10. // stem can be set.
  11. var order = ['history', 'path', 'basename', 'stem', 'extname', 'dirname']
  12. proto.toString = toString
  13. // Access full path (`~/index.min.js`).
  14. Object.defineProperty(proto, 'path', {get: getPath, set: setPath})
  15. // Access parent path (`~`).
  16. Object.defineProperty(proto, 'dirname', {get: getDirname, set: setDirname})
  17. // Access basename (`index.min.js`).
  18. Object.defineProperty(proto, 'basename', {get: getBasename, set: setBasename})
  19. // Access extname (`.js`).
  20. Object.defineProperty(proto, 'extname', {get: getExtname, set: setExtname})
  21. // Access stem (`index.min`).
  22. Object.defineProperty(proto, 'stem', {get: getStem, set: setStem})
  23. // Construct a new file.
  24. function VFile(options) {
  25. var prop
  26. var index
  27. var length
  28. if (!options) {
  29. options = {}
  30. } else if (typeof options === 'string' || buffer(options)) {
  31. options = {contents: options}
  32. } else if ('message' in options && 'messages' in options) {
  33. return options
  34. }
  35. if (!(this instanceof VFile)) {
  36. return new VFile(options)
  37. }
  38. this.data = {}
  39. this.messages = []
  40. this.history = []
  41. this.cwd = process.cwd()
  42. // Set path related properties in the correct order.
  43. index = -1
  44. length = order.length
  45. while (++index < length) {
  46. prop = order[index]
  47. if (own.call(options, prop)) {
  48. this[prop] = options[prop]
  49. }
  50. }
  51. // Set non-path related properties.
  52. for (prop in options) {
  53. if (order.indexOf(prop) === -1) {
  54. this[prop] = options[prop]
  55. }
  56. }
  57. }
  58. function getPath() {
  59. return this.history[this.history.length - 1]
  60. }
  61. function setPath(path) {
  62. assertNonEmpty(path, 'path')
  63. if (path !== this.path) {
  64. this.history.push(path)
  65. }
  66. }
  67. function getDirname() {
  68. return typeof this.path === 'string' ? path.dirname(this.path) : undefined
  69. }
  70. function setDirname(dirname) {
  71. assertPath(this.path, 'dirname')
  72. this.path = path.join(dirname || '', this.basename)
  73. }
  74. function getBasename() {
  75. return typeof this.path === 'string' ? path.basename(this.path) : undefined
  76. }
  77. function setBasename(basename) {
  78. assertNonEmpty(basename, 'basename')
  79. assertPart(basename, 'basename')
  80. this.path = path.join(this.dirname || '', basename)
  81. }
  82. function getExtname() {
  83. return typeof this.path === 'string' ? path.extname(this.path) : undefined
  84. }
  85. function setExtname(extname) {
  86. var ext = extname || ''
  87. assertPart(ext, 'extname')
  88. assertPath(this.path, 'extname')
  89. if (ext) {
  90. if (ext.charAt(0) !== '.') {
  91. throw new Error('`extname` must start with `.`')
  92. }
  93. if (ext.indexOf('.', 1) !== -1) {
  94. throw new Error('`extname` cannot contain multiple dots')
  95. }
  96. }
  97. this.path = replace(this.path, ext)
  98. }
  99. function getStem() {
  100. return typeof this.path === 'string'
  101. ? path.basename(this.path, this.extname)
  102. : undefined
  103. }
  104. function setStem(stem) {
  105. assertNonEmpty(stem, 'stem')
  106. assertPart(stem, 'stem')
  107. this.path = path.join(this.dirname || '', stem + (this.extname || ''))
  108. }
  109. // Get the value of the file.
  110. function toString(encoding) {
  111. var value = this.contents || ''
  112. return buffer(value) ? value.toString(encoding) : String(value)
  113. }
  114. // Assert that `part` is not a path (i.e., does not contain `path.sep`).
  115. function assertPart(part, name) {
  116. if (part.indexOf(path.sep) !== -1) {
  117. throw new Error(
  118. '`' + name + '` cannot be a path: did not expect `' + path.sep + '`'
  119. )
  120. }
  121. }
  122. // Assert that `part` is not empty.
  123. function assertNonEmpty(part, name) {
  124. if (!part) {
  125. throw new Error('`' + name + '` cannot be empty')
  126. }
  127. }
  128. // Assert `path` exists.
  129. function assertPath(path, name) {
  130. if (!path) {
  131. throw new Error('Setting `' + name + '` requires `path` to be set too')
  132. }
  133. }