Skip to content

Commit 9c93331

Browse files
committed
local css
1 parent 27e3500 commit 9c93331

File tree

8 files changed

+126
-34
lines changed

8 files changed

+126
-34
lines changed

lib/css-rewriter.js

Lines changed: 0 additions & 5 deletions
This file was deleted.

lib/loader.js

Lines changed: 36 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,17 @@
11
var loaderUtils = require('loader-utils')
22
var selectorPath = require.resolve('./selector')
33
var parserPath = require.resolve('./parser')
4-
var cssRewriterPath = require.resolve('./css-rewriter')
4+
5+
var defaultLang = {
6+
template: 'html',
7+
style: 'css',
8+
script: 'js'
9+
}
10+
11+
var rewriters = {
12+
template: require.resolve('./template-rewriter') + '!',
13+
style: require.resolve('./style-rewriter') + '!'
14+
}
515

616
module.exports = function (content) {
717
var self = this
@@ -17,25 +27,13 @@ module.exports = function (content) {
1727
loaders.css = loaders.css || 'style!css'
1828
loaders.js = loaders.js || ''
1929

20-
var loaderPrefix = {
21-
template: 'html!template-html-loader?raw&engine=',
22-
style: 'style!css!',
23-
script: ''
24-
}
25-
26-
var defaultLang = {
27-
template: 'html',
28-
style: 'css',
29-
script: 'js'
30-
}
31-
32-
function getRequire (type, part, index) {
30+
function getRequire (type, part, index, local) {
3331
return 'require(' +
3432
loaderUtils.stringifyRequest(self,
3533
// disable system loaders (except post loaders)
3634
'-!' +
3735
// get loader string for pre-processors
38-
getLoaderString(type, part) +
36+
getLoaderString(type, part, local) +
3937
// select the corresponding part from the vue file
4038
getSelectorString(type, index || 0) +
4139
// the url to the actual vuefile
@@ -54,14 +52,25 @@ module.exports = function (content) {
5452
')\n'
5553
}
5654

57-
function getLoaderString (type, part) {
55+
function getLoaderString (type, part, local) {
5856
var lang = part.lang || defaultLang[type]
59-
var localCssString = type === 'style' && part.local
60-
? cssRewriterPath + '!'
61-
: ''
62-
return loaders[lang] !== undefined
63-
? loaders[lang] + '!' + localCssString
64-
: loaderPrefix[type] + localCssString + lang + '!'
57+
var rewriter = local ? rewriters[type] || '' : ''
58+
var loader = loaders[lang]
59+
if (loader !== undefined) {
60+
// lang with default or pre-configured loader
61+
if (loader) loader += '!'
62+
return loader + rewriter
63+
} else {
64+
// unknown lang, assume a loader for it is used
65+
switch (type) {
66+
case 'template':
67+
return 'html!' + rewriter + 'template-html-loader?raw&engine=' + lang + '!'
68+
case 'style':
69+
return 'style!css!' + rewriter + lang + '!'
70+
case 'script':
71+
return lang + '!'
72+
}
73+
}
6574
}
6675

6776
function getSelectorString (type, index) {
@@ -78,6 +87,7 @@ module.exports = function (content) {
7887
// parser.js on the raw vue file and get the parsing result
7988
// which is an object that contains info about the vue file.
8089
var parts = self.exec(source, url)
90+
var hasLocalStyles = false
8191

8292
// add requires for src imports
8393
parts.includes.forEach(function (include) {
@@ -86,19 +96,20 @@ module.exports = function (content) {
8696

8797
// add requires for styles
8898
parts.style.forEach(function (style, i) {
89-
output += getRequire('style', style, i)
99+
if (style.local) hasLocalStyles = true
100+
output += getRequire('style', style, i, hasLocalStyles)
90101
})
91102

92103
// only one script tag allowed
93104
if (parts.script.length) {
94105
output += 'module.exports = ' +
95-
getRequire('script', parts.script[0])
106+
getRequire('script', parts.script[0], 0, hasLocalStyles)
96107
}
97108

98109
// only one template tag allowed
99110
if (parts.template.length) {
100111
output += 'module.exports.template = ' +
101-
getRequire('template', parts.template[0])
112+
getRequire('template', parts.template[0], 0, hasLocalStyles)
102113
}
103114

104115
// done

lib/style-rewriter.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
var postcss = require('postcss')
2+
var nested = require('postcss-nested')
3+
var hash = require('hash-sum')
4+
5+
var currentClass
6+
var rootRE = /:root\b/g
7+
var processRoot = postcss.plugin('process-root', function () {
8+
return function (root) {
9+
root.each(function (node) {
10+
node.each(function (node) {
11+
if (rootRE.test(node.selector)) {
12+
// replace :root selector
13+
node.selector = node.selector.replace(rootRE, currentClass)
14+
// move the node to the outer scope to avoid nesting
15+
node.moveBefore(root.nodes[0])
16+
}
17+
})
18+
})
19+
}
20+
})
21+
22+
module.exports = function (css) {
23+
this.cacheable()
24+
var cb = this.async()
25+
var cls = currentClass = '.v-' + hash(this.resourcePath)
26+
css = cls + '{' + css + '}'
27+
postcss([processRoot, nested])
28+
.process(css)
29+
.then(function (result) {
30+
cb(null, result)
31+
})
32+
.catch(cb)
33+
}

lib/template-rewriter.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
var parse5 = require('parse5')
2+
var parser = new parse5.Parser()
3+
var serializer = new parse5.Serializer()
4+
var hash = require('hash-sum')
5+
6+
module.exports = function (html) {
7+
this.cacheable()
8+
var cls = 'v-' + hash(this.resourcePath)
9+
var tree = parser.parseFragment(html)
10+
tree.childNodes.forEach(function (node) {
11+
if (node.attrs) {
12+
var hasClass = false
13+
for (var i = 0, l = node.attrs.length; i < l; i++) {
14+
var attr = node.attrs[i]
15+
if (attr.name === 'class') {
16+
attr.value += ' ' + cls
17+
hasClass = true
18+
break
19+
}
20+
}
21+
if (!hasClass) {
22+
node.attrs.push({
23+
name: 'class',
24+
value: cls
25+
})
26+
}
27+
}
28+
})
29+
return serializer.serialize(tree)
30+
}

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,10 @@
2323
"test": "eslint lib && mocha test/test.js --slow 2000"
2424
},
2525
"dependencies": {
26+
"hash-sum": "^1.0.2",
2627
"loader-utils": "^0.2.10",
27-
"parse5": "^1.5.0"
28+
"parse5": "^1.5.0",
29+
"postcss": "^4.1.16"
2830
},
2931
"peerDependencies": {
3032
"css-loader": "^0.15.4",

test/fixtures/local-css.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
window.testModule = require('./local-css.vue')

test/fixtures/local-css.vue

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
11
<style local>
2-
h1 { color: red; }
2+
:root {
3+
color: red;
4+
}
5+
div:root.test {
6+
color: blue;
7+
}
8+
h1 {
9+
color: green;
10+
}
311
</style>
12+
13+
<template>
14+
<div><h1>hi</h1></div>
15+
<p class="abc def">hi</p>
16+
</template>

test/test.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,17 @@ describe('vue-loader', function () {
100100

101101
it('local-css', function (done) {
102102
test({
103-
entry: './test/fixtures/local-css.vue'
103+
entry: './test/fixtures/local-css.js'
104104
}, function (window) {
105+
var module = window.testModule
106+
expect(module.template).to.contain(
107+
'<div class="v-5f0cf35a"><h1>hi</h1></div>\n' +
108+
'<p class="abc def v-5f0cf35a">hi</p>'
109+
)
105110
var style = window.document.querySelector('style').textContent
106-
expect(style).to.contain('h1 { color: red; }')
111+
expect(style).to.match(/div\.v-5f0cf35a\.test {\s+color: blue;\n}/)
112+
expect(style).to.match(/\.v-5f0cf35a {\s+color: red;\n}/)
113+
expect(style).to.match(/\.v-5f0cf35a h1 {\s+color: green;\n}/)
107114
done()
108115
})
109116
})

0 commit comments

Comments
 (0)