compileStyle.ts 3.4 KB

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