8000 Load postcss config files and improved Dev Exp · meteor-vue/vue-meteor@e504b65 · GitHub
[go: up one dir, main page]

Skip to content

Commit e504b65

Browse files
author
Guillaume Chau
committed
Load postcss config files and improved Dev Exp
1 parent 0c17638 commit e504b65

File tree

4 files changed

+138
-83
lines changed

4 files changed

+138
-83
lines changed
Lines changed: 111 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,122 @@
1-
import postcss from 'postcss';
2-
import selectorParser from 'postcss-selector-parser';
1+
import postcss from 'postcss'
2+
import selectorParser from 'postcss-selector-parser'
3+
import load from 'postcss-load-config'
4+
import { Meteor } from 'meteor/meteor'
35

4-
scopeId = postcss.plugin('scope-id', function (opts) {
5-
return function (root) {
6-
var keyframes = Object.create(null)
6+
let loaded
77

8-
root.each(function rewriteSelector (node) {
9-
if (!node.selector) {
10-
// handle media queries
11-
if (node.type === 'atrule') {
12-
if (node.name === 'media') {
13-
node.each(rewriteSelector)
14-
} else if (node.name === 'keyframes') {
15-
// register keyframes
16-
keyframes[node.params] = node.params = node.params + '-' + opts.id
17-
}
8+
loadPostcssConfig = Meteor.wrapAsync(function (cb) {
9+
let error = null
10+
if (!loaded) {
11+
loaded = load().catch(err => {
12+
// postcss-load-config throws error when no config file is found,
13+
// but for us it's optional. only emit other errors
14+
if (err.message.indexOf('No PostCSS Config found') < 0) {
15+
error = err
16+
error.message = 'PostCSS config Error: '.red + error.message
17+
}
18+
})
19+
}
20+
21+
loaded.then(config => {
22+
let plugins = []
23+
let options = {}
24+
25+
// merge postcss config file
26+
if (config && config.plugins) {
27+
plugins = plugins.concat(config.plugins)
28+
}
29+
if (config && config.options) {
30+
options = Object.assign({}, config.options, options)
31+
}
32+
33+
cb(error, {
34+
plugins,
35+
options
36+
})
37+
})
38+
})
39+
40+
scopeId = postcss.plugin('add-id', ({ id }) => root => {
41+
const keyframes = Object.create(null)
42+
43+
root.each(function rewriteSelector (node) {
44+
if (!node.selector) {
45+
// handle media queries
46+
if (node.type === 'atrule') {
47+
if (node.name === 'media' || node.name === 'supports') {
48+
node.each(rewriteSelector)
49+
} else if (/-?keyframes$/.test(node.name)) {
50+
// register keyframes
51+
keyframes[node.params] = node.params = node.params + '-' + id
1852
}
19-
return
2053
}
21-
node.selector = selectorParser(function (selectors) {
22-
selectors.each(function (selector) {
23-
var node = null
24-
selector.each(function (n) {
25-
// ">>>" combinator
26-
if (n.type === 'combinator' && n.value === '>>>') {
27-
n.value = ' '
28-
n.spaces.before = n.spaces.after = ''
29-
return false
30-
}
31-
// /deep/ alias for >>>, since >>> doesn't work in SASS
32-
if (n.type === 'tag' && n.value === '/deep/') {
33-
var next = n.next()
34-
if (next.type === 'combinator' && next.value === ' ') {
35-
next.remove()
36-
}
37-
n.remove()
38-
return false
54+
return
55+
}
56+
node.selector = selectorParser(selectors => {
57+
selectors.each(selector => {
58+
let node = null
59+
selector.each(n => {
60+
// ">>>" combinator
61+
if (n.type === 'combinator' && n.value === '>>>') {
62+
n.value = ' '
63+
n.spaces.before = n.spaces.after = ''
64+
return false
65+
}
66+
// /deep/ alias for >>>, since >>> doesn't work in SASS
67+
if (n.type === 'tag' && n.value === '/deep/') {
68+
const next = n.next()
69+
if (next.type === 'combinator' && next.value === ' ') {
70+
next.remove()
3971
}
40-
if (n.type !== 'pseudo' && n.type !== 'combinator') {
41-
node = n
72+
n.remove()
73+
return false
74+
}
75+
if (n.type !== 'pseudo' && n.type !== 'combinator') {
76+
node = n
77+
}
78+
})
79+
selector.insertAfter(node, selectorParser.attribute({
80+
attribute: id
81+
}))
82+
})
83+
}).process(node.selector).result
84+
})
85+
86+
// If keyframes are found in this <style>, find and rewrite animation names
87+
// in declarations.
88+
// Caveat: this only works for keyframes and animation rules in the same
89+
// <style> element.
90+
if (Object.keys(keyframes).length) {
91+
root.walkDecls(decl => {
92+
// individual animation-name declaration
93+
if (/-?animation-name$/.test(decl.prop)) {
94+
decl.value = decl.value.split(',')
95+
.map(v => keyframes[v.trim()] || v.trim())
96+
.join(',')
97+
}
98+
// shorthand
99+
if (/-?animation$/.test(decl.prop)) {
100+
decl.value = decl.value.split(',')
101+
.map(v => {
102+
const vals = v.split(/\s+/)
103+
const name = vals[0]
104+
if (keyframes[name]) {
105+
return [keyframes[name]].concat(vals.slice(1)).join(' ')
106+
} else {
107+
return v
42108
}
43109
})
44-
selector.insertAfter(node, selectorParser.attribute({
45-
attribute: opts.id
46-
}))
47-
})
48-
}).process(node.selector).result
110+
.join(',')
111+
}
49112
})
113+
}
114+
})
50115

51-
// If keyframes are found in this <style>, find and rewrite animation names
52-
// in declarations.
53-
// Caveat: this only works for keyframes and animation rules in the same
54-
// <style> element.
55-
if (Object.keys(keyframes).length) {
56-
root.walkDecls(decl => {
57-
// individual animation-name declaration
58-
if (/-?animation-name$/.test(decl.prop)) {
59-
decl.value = decl.value.split(',')
60-
.map(v => keyframes[v.trim()] || v.trim())
61-
.join(',')
62-
}
63-
// shorthand
64-
if (/-?animation$/.test(decl.prop)) {
65-
decl.value = decl.value.split(',')
66-
.map(v => {
67-
var vals = v.split(/\s+/)
68-
var name = vals[0]
69-
if (keyframes[name]) {
70-
return [keyframes[name]].concat(vals.slice(1)).join(' ')
71-
} else {
72-
return v
73-
}
74-
})
75-
.join(',')
76-
}
77-
})
116+
trimCSS = postcss.plugin('trim', opts => css => {
117+
css.walk(({ type, raws }) => {
118+
if (type === 'rule' || type === 'atrule') {
119+
raws.before = raws.after = '\n'
78120
}
79-
}
121+
})
80122
})

packages/vue-component/plugin/tag-handler.js

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ VueComponentTagHandler = class VueComponentTagHandler {
130130
charIndex: tag.tagStartIndex,
131131
action: 'compiling',
132132
lang,
133-
message: `Can't find handler for lang ${lang}, did you install it?`,
133+
message: `Can't find handler for lang '${lang}', did you install it?`.yellow,
134134
});
135135
} else {
136136
//console.log(`Compiling <script> in lang ${lang}...`);
@@ -214,7 +214,7 @@ VueComponentTagHandler = class VueComponentTagHandler {
214214
map = maps[0]
215215
}
216216
} catch (e) {
217-
console.error(`Error while mergin sourcemaps for ${inputFilePath}`, e.message)
217+
console.error(`Error while mergin sourcemaps for ${inputFilePath}`.red, e.message)
218218
console.log(maps)
219219
map = maps[0]
220220
}
@@ -247,7 +247,7 @@ VueComponentTagHandler = class VueComponentTagHandler {
247247
charIndex: templateTag.tagStartIndex,
248248
action: 'compiling',
249249
lang,
250-
message: `Can't find handler for lang ${lang}, did you install it?`,
250+
message: `Can't find handler for lang '${lang}', did you install it?`.yellow,
251251
});
252252
} else {
253253
//console.log(`Compiling <template> in lang ${lang}...`);
@@ -305,7 +305,7 @@ VueComponentTagHandler = class VueComponentTagHandler {
305305
charIndex: styleTag.tagStartIndex,
306306
action: 'compiling',
307307
lang,
308-
message: `Can't find handler for lang ${lang}, did you install it?`,
308+
message: `Can't find handler for lang '${lang}', did you install it?`.yellow,
309309
});
310310
} else {
311311
//console.log(`Compiling <style> in lang ${lang}...`);
@@ -333,16 +333,31 @@ VueComponentTagHandler = class VueComponentTagHandler {
333333
}
334334

335335
// Postcss
336-
let plugins = [];
337-
let postcssOptions = {
336+
let plugins = []
337+
let customPostcssOptions = {}
338+
try {
339+
customPostcssOptions = loadPostcssConfig()
340+
} catch (e) {
341+
throwCompileError({
342+
inputFile: this.inputFile,
343+
tag: 'style',
344+
charIndex: styleTag.tagStartIndex,
345+
action: 'configuring PostCSS (custom configuration)',
346+
error: e,
347+
showError: true
348+
})
349+
}
350+
let postcssOptions = Object.assign({
338351
from: inputFilePath,
339352
to: inputFilePath,
340353
map: {
341354
inline: false,
342355
annotation: false,
343356
prev: cssMap
344357
}
345-
}
358+
}, customPostcssOptions)
359+
360+
plugins.push(trimCSS)
346361

347362
// Scoped
348363
if (styleTag.attribs.scoped) {

packages/vue-component/plugin/utils.js

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -153,13 +153,7 @@ throwCompileError = function throwCompileError(options) {
153153

154154
// Message
155155
if(message) {
156-
if(!action) {
157-
output += ': ';
158 BF17 -
} else {
159-
output += ' ';
160-
}
161-
162-
output += message;
156+
output += ': ' + message;
163157
}
164158

165159
let errMsg = `${output}`;

packages/vue-component/plugin/vue-compiler.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,10 @@ const hotCompile = Meteor.bindEnvironment(function hotCompile(filePath, inputFil
347347
encoding: 'utf8'
348348
});
349349
let compileResult = compileOneFileWithContents(inputFile, contents, parts, babelOptions);
350+
if (!compileResult) {
351+
return
352+
}
353+
350354
let vueId = compileResult.hash;
351355

352356
// CSS

0 commit comments

Comments
 (0)
0