You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

143 lines
3.2 KiB

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