815b87a08af7839c6abbb73d4fc13a054b3d21547dc9c3e05e9addc833ba503e2c9b2e43ee6f03b3c8c338f9392029d2da955e8219ea471a701778d77af56e 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. const path = require('path')
  2. const { getOptions } = require('loader-utils')
  3. const validateOptions = require('schema-utils')
  4. const postcss = require('postcss')
  5. const postcssrc = require('postcss-load-config')
  6. const Warning = require('./Warning.js')
  7. const SyntaxError = require('./Error.js')
  8. const parseOptions = require('./options.js')
  9. /**
  10. * **PostCSS Loader**
  11. *
  12. * Loads && processes CSS with [PostCSS](https://github.com/postcss/postcss)
  13. *
  14. * @method loader
  15. *
  16. * @param {String} css Source
  17. * @param {Object} map Source Map
  18. *
  19. * @return {cb} cb Result
  20. */
  21. function loader (css, map, meta) {
  22. const options = Object.assign({}, getOptions(this))
  23. validateOptions(require('./options.json'), options, 'PostCSS Loader')
  24. const cb = this.async()
  25. const file = this.resourcePath
  26. const sourceMap = options.sourceMap
  27. Promise.resolve().then(() => {
  28. const length = Object.keys(options)
  29. .filter((option) => {
  30. switch (option) {
  31. // case 'exec':
  32. case 'ident':
  33. case 'config':
  34. case 'sourceMap':
  35. return
  36. default:
  37. return option
  38. }
  39. })
  40. .length
  41. if (length) {
  42. return parseOptions.call(this, options)
  43. }
  44. const rc = {
  45. path: path.dirname(file),
  46. ctx: {
  47. file: {
  48. extname: path.extname(file),
  49. dirname: path.dirname(file),
  50. basename: path.basename(file)
  51. },
  52. options: {}
  53. }
  54. }
  55. if (options.config) {
  56. if (options.config.path) {
  57. rc.path = path.resolve(options.config.path)
  58. }
  59. if (options.config.ctx) {
  60. rc.ctx.options = options.config.ctx
  61. }
  62. }
  63. rc.ctx.webpack = this
  64. return postcssrc(rc.ctx, rc.path)
  65. }).then((config) => {
  66. if (!config) {
  67. config = {}
  68. }
  69. if (config.file) {
  70. this.addDependency(config.file)
  71. }
  72. // Disable override `to` option from `postcss.config.js`
  73. if (config.options.to) {
  74. delete config.options.to
  75. }
  76. // Disable override `from` option from `postcss.config.js`
  77. if (config.options.from) {
  78. delete config.options.from
  79. }
  80. let plugins = config.plugins || []
  81. let options = Object.assign({
  82. from: file,
  83. map: sourceMap
  84. ? sourceMap === 'inline'
  85. ? { inline: true, annotation: false }
  86. : { inline: false, annotation: false }
  87. : false
  88. }, config.options)
  89. // Loader Exec (Deprecated)
  90. // https://webpack.js.org/api/loaders/#deprecated-context-properties
  91. if (options.parser === 'postcss-js') {
  92. css = this.exec(css, this.resource)
  93. }
  94. if (typeof options.parser === 'string') {
  95. options.parser = require(options.parser)
  96. }
  97. if (typeof options.syntax === 'string') {
  98. options.syntax = require(options.syntax)
  99. }
  100. if (typeof options.stringifier === 'string') {
  101. options.stringifier = require(options.stringifier)
  102. }
  103. // Loader API Exec (Deprecated)
  104. // https://webpack.js.org/api/loaders/#deprecated-context-properties
  105. if (config.exec) {
  106. css = this.exec(css, this.resource)
  107. }
  108. if (sourceMap && typeof map === 'string') {
  109. map = JSON.parse(map)
  110. }
  111. if (sourceMap && map) {
  112. options.map.prev = map
  113. }
  114. return postcss(plugins)
  115. .process(css, options)
  116. .then((result) => {
  117. let { css, map, root, processor, messages } = result
  118. result.warnings().forEach((warning) => {
  119. this.emitWarning(new Warning(warning))
  120. })
  121. messages.forEach((msg) => {
  122. if (msg.type === 'dependency') {
  123. this.addDependency(msg.file)
  124. }
  125. })
  126. map = map ? map.toJSON() : null
  127. if (map) {
  128. map.file = path.resolve(map.file)
  129. map.sources = map.sources.map((src) => path.resolve(src))
  130. }
  131. if (!meta) {
  132. meta = {}
  133. }
  134. const ast = {
  135. type: 'postcss',
  136. version: processor.version,
  137. root
  138. }
  139. meta.ast = ast
  140. meta.messages = messages
  141. if (this.loaderIndex === 0) {
  142. /**
  143. * @memberof loader
  144. * @callback cb
  145. *
  146. * @param {Object} null Error
  147. * @param {String} css Result (JS Module)
  148. * @param {Object} map Source Map
  149. */
  150. cb(null, `module.exports = ${JSON.stringify(css)}`, map)
  151. return null
  152. }
  153. /**
  154. * @memberof loader
  155. * @callback cb
  156. *
  157. * @param {Object} null Error
  158. * @param {String} css Result (Raw Module)
  159. * @param {Object} map Source Map
  160. */
  161. cb(null, css, map, meta)
  162. return null
  163. })
  164. }).catch((err) => {
  165. if (err.file) {
  166. this.addDependency(err.file)
  167. }
  168. return err.name === 'CssSyntaxError'
  169. ? cb(new SyntaxError(err))
  170. : cb(err)
  171. })
  172. }
  173. /**
  174. * @author Andrey Sitnik (@ai) <andrey@sitnik.ru>
  175. *
  176. * @license MIT
  177. * @version 3.0.0
  178. *
  179. * @module postcss-loader
  180. *
  181. * @requires path
  182. *
  183. * @requires loader-utils
  184. * @requires schema-utils
  185. *
  186. * @requires postcss
  187. * @requires postcss-load-config
  188. *
  189. * @requires ./options.js
  190. * @requires ./Warning.js
  191. * @requires ./SyntaxError.js
  192. */
  193. module.exports = loader