compileStyle.ts 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. const postcss = require('postcss')
  2. import { ProcessOptions, LazyResult } from 'postcss'
  3. import trimPlugin from './stylePlugins/trim'
  4. import scopedPlugin from './stylePlugins/scoped'
  5. import {
  6. processors,
  7. StylePreprocessor,
  8. StylePreprocessorResults
  9. } from './stylePreprocessors'
  10. import { cssVarsPlugin } from './cssVars'
  11. export interface SFCStyleCompileOptions {
  12. source: string
  13. filename: string
  14. id: string
  15. map?: any
  16. scoped?: boolean
  17. trim?: boolean
  18. preprocessLang?: string
  19. preprocessOptions?: any
  20. postcssOptions?: any
  21. postcssPlugins?: any[]
  22. isProd?: boolean
  23. }
  24. export interface SFCAsyncStyleCompileOptions extends SFCStyleCompileOptions {
  25. isAsync?: boolean
  26. }
  27. export interface SFCStyleCompileResults {
  28. code: string
  29. map: any | void
  30. rawResult: LazyResult | void
  31. errors: string[]
  32. }
  33. export function compileStyle(
  34. options: SFCStyleCompileOptions
  35. ): SFCStyleCompileResults {
  36. return doCompileStyle({ ...options, isAsync: false })
  37. }
  38. export function compileStyleAsync(
  39. options: SFCStyleCompileOptions
  40. ): Promise<SFCStyleCompileResults> {
  41. return Promise.resolve(doCompileStyle({ ...options, isAsync: true }))
  42. }
  43. export function doCompileStyle(
  44. options: SFCAsyncStyleCompileOptions
  45. ): SFCStyleCompileResults {
  46. const {
  47. filename,
  48. id,
  49. scoped = true,
  50. trim = true,
  51. isProd = false,
  52. preprocessLang,
  53. postcssOptions,
  54. postcssPlugins
  55. } = options
  56. const preprocessor = preprocessLang && processors[preprocessLang]
  57. const preProcessedSource = preprocessor && preprocess(options, preprocessor)
  58. const map = preProcessedSource ? preProcessedSource.map : options.map
  59. const source = preProcessedSource ? preProcessedSource.code : options.source
  60. const plugins = (postcssPlugins || []).slice()
  61. plugins.unshift(cssVarsPlugin({ id: id.replace(/^data-v-/, ''), isProd }))
  62. if (trim) {
  63. plugins.push(trimPlugin())
  64. }
  65. if (scoped) {
  66. plugins.push(scopedPlugin(id))
  67. }
  68. const postCSSOptions: ProcessOptions = {
  69. ...postcssOptions,
  70. to: filename,
  71. from: filename
  72. }
  73. if (map) {
  74. postCSSOptions.map = {
  75. inline: false,
  76. annotation: false,
  77. prev: map
  78. }
  79. }
  80. let result, code, outMap
  81. const errors: any[] = []
  82. if (preProcessedSource && preProcessedSource.errors.length) {
  83. errors.push(...preProcessedSource.errors)
  84. }
  85. try {
  86. result = postcss(plugins).process(source, postCSSOptions)
  87. // In async mode, return a promise.
  88. if (options.isAsync) {
  89. return result
  90. .then(
  91. (result: LazyResult): SFCStyleCompileResults => ({
  92. code: result.css || '',
  93. map: result.map && result.map.toJSON(),
  94. errors,
  95. rawResult: result
  96. })
  97. )
  98. .catch(
  99. (error: Error): SFCStyleCompileResults => ({
  100. code: '',
  101. map: undefined,
  102. errors: [...errors, error.message],
  103. rawResult: undefined
  104. })
  105. )
  106. }
  107. // force synchronous transform (we know we only have sync plugins)
  108. code = result.css
  109. outMap = result.map
  110. } catch (e) {
  111. errors.push(e)
  112. }
  113. return {
  114. code: code || ``,
  115. map: outMap && outMap.toJSON(),
  116. errors,
  117. rawResult: result
  118. }
  119. }
  120. function preprocess(
  121. options: SFCStyleCompileOptions,
  122. preprocessor: StylePreprocessor
  123. ): StylePreprocessorResults {
  124. return preprocessor(
  125. options.source,
  126. options.map,
  127. Object.assign(
  128. {
  129. filename: options.filename
  130. },
  131. options.preprocessOptions
  132. )
  133. )
  134. }