UNPKG

4.12 kBJavaScriptView Raw
1import { existsSync, mkdirSync, writeFile } from 'fs'
2import { dirname } from 'path'
3import { createFilter } from 'rollup-pluginutils'
4
5export default function css (options = {}) {
6 const filter = createFilter(options.include || ['/**/*.css', '/**/*.scss', '/**/*.sass'], options.exclude)
7 let dest = options.output
8
9 const styles = {}
10 let includePaths = options.includePaths || []
11 includePaths.push(process.cwd())
12
13 const compileToCSS = function (scss) {
14 // Compile SASS to CSS
15 if (scss.length) {
16 includePaths = includePaths.filter((v, i, a) => a.indexOf(v) === i)
17 try {
18 const css = require('node-sass').renderSync(Object.assign({
19 data: scss,
20 includePaths
21 }, options)).css.toString()
22 // Possibly process CSS (e.g. by PostCSS)
23 if (typeof options.processor === 'function') {
24 return options.processor(css, styles)
25 }
26 return css
27 } catch (e) {
28 if (options.failOnError) {
29 throw e
30 }
31 console.log()
32 console.log(red('Error:\n\t' + e.message))
33 if (e.message.includes('Invalid CSS')) {
34 console.log(green('Solution:\n\t' + 'fix your Sass code'))
35 console.log('Line: ' + e.line)
36 console.log('Column: ' + e.column)
37 }
38 if (e.message.includes('node-sass') && e.message.includes('find module')) {
39 console.log(green('Solution:\n\t' + 'npm install --save node-sass'))
40 }
41 if (e.message.includes('node-sass') && e.message.includes('bindigs')) {
42 console.log(green('Solution:\n\t' + 'npm rebuild node-sass --force'))
43 }
44 console.log()
45 }
46 }
47 }
48
49 return {
50 name: 'css',
51 transform (code, id) {
52 if (!filter(id)) {
53 return
54 }
55
56 // When output is disabled, the stylesheet is exported as a string
57 if (options.output === false) {
58 const css = compileToCSS(code)
59 return {
60 code: 'export default ' + JSON.stringify(css),
61 map: { mappings: '' }
62 }
63 }
64
65 // Map of every stylesheet
66 styles[id] = code
67 includePaths.push(dirname(id))
68
69 return ''
70 },
71 generateBundle (opts) {
72 // No stylesheet needed
73 if (options.output === false) {
74 return
75 }
76
77 // Combine all stylesheets
78 let scss = ''
79 for (const id in styles) {
80 scss += styles[id] || ''
81 }
82
83 const css = compileToCSS(scss)
84
85 // Resolve if porcessor returned a Promise
86 Promise.resolve(css).then(css => {
87 // Emit styles through callback
88 if (typeof options.output === 'function') {
89 options.output(css, styles)
90 return
91 }
92
93 if (typeof dest !== 'string') {
94 // Don't create unwanted empty stylesheets
95 if (!css.length) {
96 return
97 }
98
99 // Guess destination filename
100 dest = opts.dest || opts.file || 'bundle.js'
101 if (dest.endsWith('.js')) {
102 dest = dest.slice(0, -3)
103 }
104 dest = dest + '.css'
105 }
106
107 // Ensure that dest parent folders exist (create the missing ones)
108 ensureParentDirsSync(dirname(dest))
109
110 // Emit styles to file
111 writeFile(dest, css, (err) => {
112 if (opts.verbose !== false) {
113 if (err) {
114 console.error(red(err))
115 } else if (css) {
116 console.log(green(dest), getSize(css.length))
117 }
118 }
119 })
120 })
121 }
122 }
123}
124
125function red (text) {
126 return '\x1b[1m\x1b[31m' + text + '\x1b[0m'
127}
128
129function green (text) {
130 return '\x1b[1m\x1b[32m' + text + '\x1b[0m'
131}
132
133function getSize (bytes) {
134 return bytes < 10000
135 ? bytes.toFixed(0) + ' B'
136 : bytes < 1024000
137 ? (bytes / 1024).toPrecision(3) + ' kB'
138 : (bytes / 1024 / 1024).toPrecision(4) + ' MB'
139}
140
141function ensureParentDirsSync (dir) {
142 if (existsSync(dir)) {
143 return
144 }
145
146 try {
147 mkdirSync(dir)
148 } catch (err) {
149 if (err.code === 'ENOENT') {
150 ensureParentDirsSync(dirname(dir))
151 ensureParentDirsSync(dir)
152 }
153 }
154}