index.js 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. var Stream = require('stream')
  2. var postcss = require('postcss')
  3. var applySourceMap = require('vinyl-sourcemaps-apply')
  4. var fancyLog = require('fancy-log')
  5. var PluginError = require('plugin-error')
  6. var path = require('path')
  7. module.exports = withConfigLoader(function (loadConfig) {
  8. var stream = new Stream.Transform({ objectMode: true })
  9. stream._transform = function (file, encoding, cb) {
  10. if (file.isNull()) {
  11. return cb(null, file)
  12. }
  13. if (file.isStream()) {
  14. return handleError('Streams are not supported!')
  15. }
  16. // Protect `from` and `map` if using gulp-sourcemaps
  17. var isProtected = file.sourceMap
  18. ? { from: true, map: true }
  19. : {}
  20. var options = {
  21. from: file.path,
  22. to: file.path,
  23. // Generate a separate source map for gulp-sourcemaps
  24. map: file.sourceMap ? { annotation: false } : false
  25. }
  26. loadConfig(file)
  27. .then(function (config) {
  28. var configOpts = config.options || {}
  29. // Extend the default options if not protected
  30. for (var opt in configOpts) {
  31. if (configOpts.hasOwnProperty(opt) && !isProtected[opt]) {
  32. options[opt] = configOpts[opt]
  33. } else {
  34. fancyLog.info(
  35. 'gulp-postcss:',
  36. file.relative + '\nCannot override ' + opt +
  37. ' option, because it is required by gulp-sourcemaps'
  38. )
  39. }
  40. }
  41. return postcss(config.plugins || [])
  42. .process(file.contents, options)
  43. })
  44. .then(handleResult, handleError)
  45. function handleResult (result) {
  46. var map
  47. var warnings = result.warnings().join('\n')
  48. file.contents = Buffer.from(result.css)
  49. // Apply source map to the chain
  50. if (file.sourceMap) {
  51. map = result.map.toJSON()
  52. map.file = file.relative
  53. map.sources = [].map.call(map.sources, function (source) {
  54. return path.join(path.dirname(file.relative), source)
  55. })
  56. applySourceMap(file, map)
  57. }
  58. if (warnings) {
  59. fancyLog.info('gulp-postcss:', file.relative + '\n' + warnings)
  60. }
  61. setImmediate(function () {
  62. cb(null, file)
  63. })
  64. }
  65. function handleError (error) {
  66. var errorOptions = { fileName: file.path, showStack: true }
  67. if (error.name === 'CssSyntaxError') {
  68. errorOptions.error = error
  69. errorOptions.fileName = error.file || file.path
  70. errorOptions.lineNumber = error.line
  71. errorOptions.showProperties = false
  72. errorOptions.showStack = false
  73. error = error.message + '\n\n' + error.showSourceCode() + '\n'
  74. }
  75. // Prevent stream’s unhandled exception from
  76. // being suppressed by Promise
  77. setImmediate(function () {
  78. cb(new PluginError('gulp-postcss', error, errorOptions))
  79. })
  80. }
  81. }
  82. return stream
  83. })
  84. function withConfigLoader(cb) {
  85. return function (plugins, options) {
  86. if (Array.isArray(plugins)) {
  87. return cb(function () {
  88. return Promise.resolve({
  89. plugins: plugins,
  90. options: options
  91. })
  92. })
  93. } else if (typeof plugins === 'function') {
  94. return cb(function (file) {
  95. return Promise.resolve(plugins(file))
  96. })
  97. } else {
  98. var postcssLoadConfig = require('postcss-load-config')
  99. var contextOptions = plugins || {}
  100. return cb(function(file) {
  101. var configPath
  102. if (contextOptions.config) {
  103. if (path.isAbsolute(contextOptions.config)) {
  104. configPath = contextOptions.config
  105. } else {
  106. configPath = path.join(file.base, contextOptions.config)
  107. }
  108. } else {
  109. configPath = file.dirname
  110. }
  111. return postcssLoadConfig(
  112. {
  113. file: file,
  114. options: contextOptions
  115. },
  116. configPath
  117. )
  118. })
  119. }
  120. }
  121. }