From deac8b5f35c7b0563c6cbb9f6245007153cb5490 Mon Sep 17 00:00:00 2001
From: YiSiWang <597222964@qq.com>
Date: Mon, 29 Aug 2016 15:30:48 +0800
Subject: [PATCH 01/16] add simple support for CSS modules
---
lib/htmlCssModuleRewriter.js | 36 ++++++++++++++++++++++++
lib/loader.js | 54 +++++++++++++++++++++++++-----------
lib/parser.js | 5 +++-
test/test.js | 2 +-
4 files changed, 79 insertions(+), 18 deletions(-)
create mode 100644 lib/htmlCssModuleRewriter.js
diff --git a/lib/htmlCssModuleRewriter.js b/lib/htmlCssModuleRewriter.js
new file mode 100644
index 000000000..6d0a41f7a
--- /dev/null
+++ b/lib/htmlCssModuleRewriter.js
@@ -0,0 +1,36 @@
+var parse5 = require('parse5')
+
+module.exports = function (html, modules) {
+ var tree = parse5.parseFragment(html)
+ walk(tree, function (node) {
+ if (!node.attrs) return
+ node.attrs.forEach(function (attr) {
+ if (attr.name !== 'class') return
+ var arr = attr.value.split('.')
+ var module = arr[0]
+ var className = arr[1]
+ if (!className) return
+ var map = modules[module]
+ if (!map) return console.error('xxx')
+ if (!map[className]) return console.error('yyy')
+ attr.value = map[className]
+ })
+ })
+ return parse5.serialize(tree)
+}
+
+function walk (tree, fn) {
+ if (tree.childNodes) {
+ tree.childNodes.forEach(function (node) {
+ var isTemplate = node.tagName === 'template'
+ if (!isTemplate) {
+ fn(node)
+ }
+ if (isTemplate && node.content) {
+ walk(node.content, fn)
+ } else {
+ walk(node, fn)
+ }
+ })
+ }
+}
diff --git a/lib/loader.js b/lib/loader.js
index f6e479a53..8cd35a354 100644
--- a/lib/loader.js
+++ b/lib/loader.js
@@ -22,7 +22,7 @@ var templateLoader = require.resolve('./template-loader')
module.exports = function (content) {
var defaultLoaders = {
html: 'vue-html-loader',
- css: 'vue-style-loader!css-loader',
+ css: 'vue-style-loader!css-loader?-sourceMap',
js: 'babel-loader?presets[]=es2015&plugins[]=transform-runtime&comments=false'
}
@@ -46,25 +46,25 @@ module.exports = function (content) {
options.cssSourceMap !== false &&
process.env.NODE_ENV !== 'production'
) {
- defaultLoaders.css = 'vue-style-loader!css-loader?sourceMap'
+ defaultLoaders.css = 'vue-style-loader!css-loader?+sourceMap'
}
// check if there are custom loaders specified via
// webpack config, otherwise use defaults
var loaders = assign({}, defaultLoaders, options.loaders)
- function getRequire (type, part, index, scoped) {
+ function getRequire (type, part, index, scoped, module) {
return 'require(' +
- getRequireString(type, part, index, scoped) +
+ getRequireString(type, part, index, scoped, module) +
')\n'
}
- function getRequireString (type, part, index, scoped) {
+ function getRequireString (type, part, index, scoped, module) {
return loaderUtils.stringifyRequest(loaderContext,
// disable all configuration loaders
'!!' +
// get loader string for pre-processors
- getLoaderString(type, part, scoped) +
+ getLoaderString(type, part, scoped, module) +
// select the corresponding part from the vue file
getSelectorString(type, index || 0) +
// the url to the actual vuefile
@@ -72,26 +72,29 @@ module.exports = function (content) {
)
}
- function getRequireForImport (type, impt, scoped) {
+ function getRequireForImport (type, impt, scoped, module) {
return 'require(' +
- getRequireForImportString(type, impt, scoped) +
+ getRequireForImportString(type, impt, scoped, module) +
')\n'
}
- function getRequireForImportString (type, impt, scoped) {
+ function getRequireForImportString (type, impt, scoped, module) {
return loaderUtils.stringifyRequest(loaderContext,
'!!' +
- getLoaderString(type, impt, scoped) +
+ getLoaderString(type, impt, scoped, module) +
impt.src
)
}
- function getLoaderString (type, part, scoped) {
+ function getLoaderString (type, part, scoped, module) {
var lang = part.lang || defaultLang[type]
var loader = loaders[lang]
- var rewriter = getRewriter(type, scoped)
+ var rewriter = getRewriter(type, scoped, module)
var injectString = (type === 'script' && query.inject) ? 'inject!' : ''
if (loader !== undefined) {
+ if (module && (type === 'style')) {
+ loader += '&modules'
+ }
// inject rewriter before css/html loader for
// extractTextPlugin use cases
if (rewriterInjectRE.test(loader)) {
@@ -115,7 +118,7 @@ module.exports = function (content) {
}
}
- function getRewriter (type, scoped) {
+ function getRewriter (type, scoped, module) {
var meta = '?id=' + moduleId
switch (type) {
case 'template':
@@ -143,7 +146,8 @@ module.exports = function (content) {
var parts = parse(content, fileName, this.sourceMap)
var hasLocalStyles = false
- var output = 'var __vue_script__, __vue_template__\n'
+ var output = 'var __vue_script__, __vue_template__\n' +
+ 'var __vue_styles__ = {}\n'
// check if there are any template syntax errors
var templateWarnings = parts.template.length && parts.template[0].warnings
@@ -151,16 +155,31 @@ module.exports = function (content) {
templateWarnings.forEach(this.emitError)
}
+ var cssModules = {}
+ function setCssModule (style, require, context) {
+ if (!style.module) return require
+ if (!(/^[a-zA-Z][a-zA-Z0-9]*$/.test(style.module))) {
+ context.emitError('Invallid CSS module name "' + style.module + '"!')
+ return require
+ }
+ if (style.module in cssModules) {
+ context.emitError('CSS module name "' + style.module + '" is not unique!')
+ return require
+ }
+ cssModules[style.module] = true
+ return '__vue_styles__["' + style.module + '"] = ' + require + '\n'
+ }
+
// add requires for src imports
parts.styleImports.forEach(function (impt) {
if (impt.scoped) hasLocalStyles = true
- output += getRequireForImport('style', impt, impt.scoped)
+ output += setCssModule(impt, getRequireForImport('style', impt, impt.scoped, impt.module), loaderContext) || ''
})
// add requires for styles
parts.style.forEach(function (style, i) {
if (style.scoped) hasLocalStyles = true
- output += getRequire('style', style, i, style.scoped)
+ output += setCssModule(style, getRequire('style', style, i, style.scoped, style.module), loaderContext) || ''
})
// add require for script
@@ -189,6 +208,7 @@ module.exports = function (content) {
// add require for template
var template
+ var rewriter = 'require("' + require.resolve('./htmlCssModuleRewriter') + '")'
if (parts.template.length) {
template = parts.template[0]
output += '__vue_template__ = ' + (
@@ -196,6 +216,8 @@ module.exports = function (content) {
? getRequireForImport('template', template, hasLocalStyles)
: getRequire('template', template, 0, hasLocalStyles)
)
+ output += 'console.warn(__vue_template__)\n'
+ output += '__vue_template__ = ' + rewriter + '(__vue_template__, __vue_styles__)\n'
}
if (!query.inject) {
diff --git a/lib/parser.js b/lib/parser.js
index a353635d1..1d338771f 100644
--- a/lib/parser.js
+++ b/lib/parser.js
@@ -40,6 +40,7 @@ module.exports = function (content, filename, needMap) {
var lang = getAttribute(node, 'lang')
var src = getAttribute(node, 'src')
var scoped = getAttribute(node, 'scoped') != null
+ var module = getAttribute(node, 'module') || ''
var warnings = null
var map = null
@@ -64,7 +65,8 @@ module.exports = function (content, filename, needMap) {
output.styleImports.push({
src: src,
lang: lang,
- scoped: scoped
+ scoped: scoped,
+ module: module
})
} else if (type === 'template') {
output.template.push({
@@ -151,6 +153,7 @@ module.exports = function (content, filename, needMap) {
output[type].push({
lang: lang,
scoped: scoped,
+ module: module,
content: result,
map: map && map.toJSON(),
warnings: warnings
diff --git a/test/test.js b/test/test.js
index 8644b3ef1..53ad1d434 100644
--- a/test/test.js
+++ b/test/test.js
@@ -34,7 +34,7 @@ describe('vue-loader', function () {
function getFile (file, cb) {
fs.readFile(path.resolve(outputDir, file), 'utf-8', function (err, data) {
- expect(err).to.be.not.exist
+ expect(err).to.not.exist
cb(data)
})
}
From c9516532cea2596fe89e8a182584ca7848c58941 Mon Sep 17 00:00:00 2001
From: YiSiWang <597222964@qq.com>
Date: Mon, 29 Aug 2016 15:55:48 +0800
Subject: [PATCH 02/16] add test case
---
README.md | 40 +++++++++++++++++++++++++++++++++++
test/fixtures/css-modules.vue | 13 ++++++++++++
test/test.js | 16 ++++++++++++++
3 files changed, 69 insertions(+)
create mode 100644 test/fixtures/css-modules.vue
diff --git a/README.md b/README.md
index a4178a27d..92a4067ef 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,43 @@
+# Add CSS modules support
+
+Covert
+
+```html
+
+
+
+
+
+
+
+```
+
+to:
+
+```html
+
+
+
+
+
+
+
+```
+
+# 下面是原来的 README
+
# vue-loader [](https://circleci.com/gh/vuejs/vue-loader/tree/master) [](https://www.npmjs.com/package/vue-loader)
> Vue.js component loader for [Webpack](http://webpack.github.io).
diff --git a/test/fixtures/css-modules.vue b/test/fixtures/css-modules.vue
new file mode 100644
index 000000000..b796c5772
--- /dev/null
+++ b/test/fixtures/css-modules.vue
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
diff --git a/test/test.js b/test/test.js
index 53ad1d434..2a735b67f 100644
--- a/test/test.js
+++ b/test/test.js
@@ -276,4 +276,20 @@ describe('vue-loader', function () {
done()
})
})
+
+ it.only('css-modules', function (done) {
+ test({
+ entry: './test/fixtures/css-modules.vue'
+ }, function (window) {
+ var module = window.vueModule
+ var match = module.template.match(/\s*
<\/h2>/)
+ expect(match).to.have.length(2)
+ var className = match[1]
+ expect(className).to.not.equal('red')
+ expect(className).to.match(/^_/)
+ var style = window.document.querySelector('style').textContent
+ expect(style).to.contain('.' + className + ' {\n color: red;\n}')
+ done()
+ })
+ })
})
From 14712894e3dd196b345cc5fb9d707def78c67048 Mon Sep 17 00:00:00 2001
From: YiSiWang <597222964@qq.com>
Date: Mon, 29 Aug 2016 15:58:54 +0800
Subject: [PATCH 03/16] add README
---
README.md | 44 +++++++++++++++++++++++++++++++----
test/fixtures/css-modules.vue | 6 +++++
test/test.js | 1 -
3 files changed, 45 insertions(+), 6 deletions(-)
diff --git a/README.md b/README.md
index 92a4067ef..e40285e78 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,8 @@
-# Add CSS modules support
+# Add CSS modules support
-Covert
+## Basic
+
+Convert
```html
-
+
```
-# 下面是原来的 README
+## Scoped animation name
+
+```html
+
+```
+
+becomes:
+
+```html
+
+```
+
+# TODO
+1. convert static class name in binding class.
+2. provide style entry in `script`
+
+Note: properties in `animation` shorthand should be in correct order.
+
+# Original README below:
# vue-loader [](https://circleci.com/gh/vuejs/vue-loader/tree/master) [](https://www.npmjs.com/package/vue-loader)
diff --git a/test/fixtures/css-modules.vue b/test/fixtures/css-modules.vue
index b796c5772..d02b0eba3 100644
--- a/test/fixtures/css-modules.vue
+++ b/test/fixtures/css-modules.vue
@@ -2,6 +2,12 @@
.red {
color: red;
}
+@keyframes fade {
+ from { opacity: 1; } to { opacity: 0; }
+}
+.animate {
+ animation: fade 1s;
+}
diff --git a/test/test.js b/test/test.js
index 2a735b67f..1f3c04b2a 100644
--- a/test/test.js
+++ b/test/test.js
@@ -285,7 +285,6 @@ describe('vue-loader', function () {
var match = module.template.match(/\s*<\/h2>/)
expect(match).to.have.length(2)
var className = match[1]
- expect(className).to.not.equal('red')
expect(className).to.match(/^_/)
var style = window.document.querySelector('style').textContent
expect(style).to.contain('.' + className + ' {\n color: red;\n}')
From f03b5c55a2c0657acb4ae7047290878f48c5820e Mon Sep 17 00:00:00 2001
From: YiSiWang <597222964@qq.com>
Date: Tue, 30 Aug 2016 11:31:34 +0800
Subject: [PATCH 04/16] add static class replacement
---
README.md | 87 ++++++++++++-----------------------
lib/htmlCssModuleRewriter.js | 59 ++++++++++++++++--------
test/fixtures/css-modules.vue | 4 ++
test/test.js | 23 +++++++++
4 files changed, 97 insertions(+), 76 deletions(-)
diff --git a/README.md b/README.md
index e40285e78..c92600690 100644
--- a/README.md
+++ b/README.md
@@ -1,74 +1,47 @@
# Add CSS modules support
-## Basic
-
-Convert
+## Overview
```html
+
+
+
-
-
-
-```
-
-to:
-
-```html
-
-
-
+
+
+
+
+
+
+
-
-
-```
-
-## Scoped animation name
-
-```html
-
-```
-
-becomes:
-
-```html
-
```
# TODO
-1. convert static class name in binding class.
-2. provide style entry in `script`
-
-Note: properties in `animation` shorthand should be in correct order.
+- [x] convert static class name in binding class.
+- [ ] provide style entry in `script`
# Original README below:
diff --git a/lib/htmlCssModuleRewriter.js b/lib/htmlCssModuleRewriter.js
index 6d0a41f7a..3902a3ff2 100644
--- a/lib/htmlCssModuleRewriter.js
+++ b/lib/htmlCssModuleRewriter.js
@@ -1,24 +1,5 @@
var parse5 = require('parse5')
-module.exports = function (html, modules) {
- var tree = parse5.parseFragment(html)
- walk(tree, function (node) {
- if (!node.attrs) return
- node.attrs.forEach(function (attr) {
- if (attr.name !== 'class') return
- var arr = attr.value.split('.')
- var module = arr[0]
- var className = arr[1]
- if (!className) return
- var map = modules[module]
- if (!map) return console.error('xxx')
- if (!map[className]) return console.error('yyy')
- attr.value = map[className]
- })
- })
- return parse5.serialize(tree)
-}
-
function walk (tree, fn) {
if (tree.childNodes) {
tree.childNodes.forEach(function (node) {
@@ -34,3 +15,43 @@ function walk (tree, fn) {
})
}
}
+
+module.exports = function (html, modules) {
+ function convert (className) {
+ className = className.trim()
+ if (!className) return ''
+ var arr = className.split('.')
+ var module = arr[0]
+ className = arr[1]
+ if (!className) return ''
+ var map = modules[module]
+ if (!map) return 'xxx'
+ if (!map[className]) return 'yyy'
+ return map[className]
+ }
+
+ var tree = parse5.parseFragment(html)
+ walk(tree, function (node) {
+ if (!node.attrs) return
+ node.attrs.forEach(function (attr) {
+ var expression = ''
+ if (attr.name === 'class') {
+ var match = attr.value.match(/^\s*({{.+}})\s*$/)
+ if (!match) {
+ attr.value = convert(attr.value)
+ return
+ }
+ expression = match[1]
+ } else {
+ if (attr.name === 'v-bind:class' || attr.name === ':class') {
+ expression = attr.value
+ }
+ }
+ if (!expression) return
+ attr.value = expression.replace(/'(.+?)'/g, function (match, className) {
+ return "'" + convert(className) + "'"
+ })
+ })
+ })
+ return parse5.serialize(tree)
+}
diff --git a/test/fixtures/css-modules.vue b/test/fixtures/css-modules.vue
index d02b0eba3..5c9435166 100644
--- a/test/fixtures/css-modules.vue
+++ b/test/fixtures/css-modules.vue
@@ -12,6 +12,10 @@
+
+
+
+
```
# TODO
diff --git a/lib/htmlCssModuleRewriter.js b/lib/htmlCssModuleRewriter.js
index 3902a3ff2..da129f005 100644
--- a/lib/htmlCssModuleRewriter.js
+++ b/lib/htmlCssModuleRewriter.js
@@ -18,15 +18,17 @@ function walk (tree, fn) {
module.exports = function (html, modules) {
function convert (className) {
- className = className.trim()
- if (!className) return ''
- var arr = className.split('.')
+ var arr = className.trim().split('.')
+ if (arr.length !== 2) return className
var module = arr[0]
className = arr[1]
- if (!className) return ''
var map = modules[module]
- if (!map) return 'xxx'
- if (!map[className]) return 'yyy'
+ if (!map) {
+ throw new Error('CSS Module name "' + module + '" is not defined!')
+ }
+ if (!map[className]) {
+ throw new Error('Class name "' + className + '" is not defined in module "' + module + '"!')
+ }
return map[className]
}
diff --git a/lib/loader.js b/lib/loader.js
index 8cd35a354..e4a560799 100644
--- a/lib/loader.js
+++ b/lib/loader.js
@@ -225,10 +225,18 @@ module.exports = function (content) {
output +=
'module.exports = __vue_script__ || {}\n' +
'if (module.exports.__esModule) module.exports = module.exports.default\n' +
+ 'var __vue_options__ = typeof module.exports === "function" ' +
+ '? (module.exports.options || (module.exports.options = {})) ' +
+ ': module.exports\n' +
'if (__vue_template__) {\n' +
- '(typeof module.exports === "function" ' +
- '? (module.exports.options || (module.exports.options = {})) ' +
- ': module.exports).template = __vue_template__\n' +
+ '__vue_options__.template = __vue_template__\n' +
+ '}\n' +
+ 'if (__vue_options__.loader) {\n' +
+ 'var $styles = __vue_options__.loader.styles\n' +
+ 'if ($styles) {\n' +
+ '(__vue_options__.computed || (__vue_options__.computed = {}))[$styles] = ' +
+ 'function () { return __vue_styles__ }\n' +
+ '}\n' +
'}\n'
// hot reload
if (
diff --git a/test/fixtures/css-modules.vue b/test/fixtures/css-modules.vue
index 5c9435166..52098ed6b 100644
--- a/test/fixtures/css-modules.vue
+++ b/test/fixtures/css-modules.vue
@@ -15,9 +15,12 @@
-
diff --git a/test/test.js b/test/test.js
index fbd9e965c..aa4997e6c 100644
--- a/test/test.js
+++ b/test/test.js
@@ -277,12 +277,11 @@ describe('vue-loader', function () {
})
})
- it.only('css-modules', function (done) {
+ it('css-modules', function (done) {
test({
entry: './test/fixtures/css-modules.vue'
}, function (window) {
var module = window.vueModule
- console.log(module.template)
// get local class name
var match = module.template.match(/\s*<\/h2>/)
@@ -311,6 +310,9 @@ describe('vue-loader', function () {
).replace(/style\.red/g, className)
expect(module.template).to.contain(expected)
+ // get local class name in script
+ expect(module.computed.$styles().style.red).to.equal(className)
+
done()
})
})
From 3944b328bbfa77d636cc4c6d9344f642887900b7 Mon Sep 17 00:00:00 2001
From: YiSiWang <597222964@qq.com>
Date: Tue, 30 Aug 2016 13:49:16 +0800
Subject: [PATCH 06/16] update README
---
README.md | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 191cfab94..f8779d81a 100644
--- a/README.md
+++ b/README.md
@@ -55,7 +55,8 @@
# TODO
- [x] convert static class name in binding class.
-- [ ] provide style entry in `script`
+- [x] provide style entry in `script`
+- [ ] write document
# Original README below:
From bfe2ceb15dabc36a8e707f37dfee1dd037a3ff7d Mon Sep 17 00:00:00 2001
From: YiSiWang <597222964@qq.com>
Date: Tue, 30 Aug 2016 15:56:54 +0800
Subject: [PATCH 07/16] update README
---
README.md | 95 ++++++++++++++++++++++++++++++++++++-------------------
1 file changed, 62 insertions(+), 33 deletions(-)
diff --git a/README.md b/README.md
index f8779d81a..b46f84d33 100644
--- a/README.md
+++ b/README.md
@@ -1,63 +1,92 @@
# Add CSS modules support
-## Overview
+## Basic
+
+Simply use [CSS modules](https://github.com/css-modules/css-modules) with `
+
+
+
+```
- /* global class name */
- :global(.red) { color: red; }
- /* is converted to */
- .red { color: red; }
+becomes:
- /* animation name */
- @keyframes fade { from { opacity: 1; } to { opacity: 0; } }
- .animate { animation: fade 1s; }
- /* is converted to */
- @keyframes n5Q3vnbE7aL9uu6uOUOLo { from { opacity: 1; } to { opacity: 0; } }
- ._2hSs7mCBtiABMJoqSnwHAD { animation: n5Q3vnbE7aL9uu6uOUOLo 1s; }
- /* Note: properties in `animation` shorthand should be in correct order. */
+```html
+
+
+
+
+```
+
+Class names are unique. So your style won't affect any other component.
+
+Tips:
+
+1. With `module`, animation names are also converted. Feel free to write short class & animation names!
+2. With `module` + `scoped`, you can limit your style to your component more strictly.
+3. With `module` only, you can style `` in your component.
+
+## Binding classes
+*Static* class names in binding expression are converted, too.
+
+```html
-
-
-
-
-
-
+
+
+
+```
+
+becomes:
+
+```html
+
-
+
+
+```
+
+Note: Here, "static class name" means class name in single quotes.
+
+Complex ones (eg. `'a' + 'b'`) are not supported (and not recommend in Vue).
+
+## Getting local class name in script
+
+In some cases, you will like to control your class name in script.
+You can do it like this:
-
+```html
+
+
+
```
-# TODO
-- [x] convert static class name in binding class.
-- [x] provide style entry in `script`
-- [ ] write document
-
# Original README below:
# vue-loader [](https://circleci.com/gh/vuejs/vue-loader/tree/master) [](https://www.npmjs.com/package/vue-loader)
From 3b3d0aa6b10854cfd3dc7f5587bd89e87cbfd7b4 Mon Sep 17 00:00:00 2001
From: YiSiWang <597222964@qq.com>
Date: Tue, 30 Aug 2016 16:57:51 +0800
Subject: [PATCH 08/16] improve test case
---
test/fixtures/css-modules.vue | 3 ++-
test/test.js | 3 ++-
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/test/fixtures/css-modules.vue b/test/fixtures/css-modules.vue
index 52098ed6b..30bd9c288 100644
--- a/test/fixtures/css-modules.vue
+++ b/test/fixtures/css-modules.vue
@@ -14,7 +14,8 @@
-
+
+
diff --git a/test/test.js b/test/test.js
index aab1c3c07..a085e1f43 100644
--- a/test/test.js
+++ b/test/test.js
@@ -284,10 +284,7 @@ describe('vue-loader', function () {
var module = window.vueModule
// get local class name
- var match = module.template.match(/\s*<\/h2>/)
- expect(match).to.have.length(2)
- var className = match[1]
- expect(className).to.not.equal('style.red')
+ var className = module.computed.style().red
expect(className).to.match(/^_/)
// class name in style
@@ -295,25 +292,12 @@ describe('vue-loader', function () {
expect(style).to.contain('.' + className + ' {\n color: red;\n}')
// animation name
- match = style.match(/@-webkit-keyframes\s+(\S+)\s+{/)
+ var match = style.match(/@-webkit-keyframes\s+(\S+)\s+{/)
expect(match).to.have.length(2)
var animationName = match[1]
expect(animationName).to.not.equal('fade')
expect(style).to.contain('animation: ' + animationName + ' 1s;')
- // static class name replacement
- var expected = (
- '\n' +
- '\n' +
- '\n' +
- '\n' +
- ''
- ).replace(/style\.red/g, className)
- expect(module.template).to.contain(expected)
-
- // get local class name in script
- expect(module.computed.$styles().style.red).to.equal(className)
-
done()
})
})
From dfdf835800f6398d0fc56ae2f7c229799037dae1 Mon Sep 17 00:00:00 2001
From: YiSiWang <597222964@qq.com>
Date: Wed, 31 Aug 2016 11:13:37 +0800
Subject: [PATCH 10/16] remove class replacement
---
lib/htmlCssModuleRewriter.js | 59 ------------------------------------
lib/loader.js | 16 ++++------
2 files changed, 6 insertions(+), 69 deletions(-)
delete mode 100644 lib/htmlCssModuleRewriter.js
diff --git a/lib/htmlCssModuleRewriter.js b/lib/htmlCssModuleRewriter.js
deleted file mode 100644
index da129f005..000000000
--- a/lib/htmlCssModuleRewriter.js
+++ /dev/null
@@ -1,59 +0,0 @@
-var parse5 = require('parse5')
-
-function walk (tree, fn) {
- if (tree.childNodes) {
- tree.childNodes.forEach(function (node) {
- var isTemplate = node.tagName === 'template'
- if (!isTemplate) {
- fn(node)
- }
- if (isTemplate && node.content) {
- walk(node.content, fn)
- } else {
- walk(node, fn)
- }
- })
- }
-}
-
-module.exports = function (html, modules) {
- function convert (className) {
- var arr = className.trim().split('.')
- if (arr.length !== 2) return className
- var module = arr[0]
- className = arr[1]
- var map = modules[module]
- if (!map) {
- throw new Error('CSS Module name "' + module + '" is not defined!')
- }
- if (!map[className]) {
- throw new Error('Class name "' + className + '" is not defined in module "' + module + '"!')
- }
- return map[className]
- }
-
- var tree = parse5.parseFragment(html)
- walk(tree, function (node) {
- if (!node.attrs) return
- node.attrs.forEach(function (attr) {
- var expression = ''
- if (attr.name === 'class') {
- var match = attr.value.match(/^\s*({{.+}})\s*$/)
- if (!match) {
- attr.value = convert(attr.value)
- return
- }
- expression = match[1]
- } else {
- if (attr.name === 'v-bind:class' || attr.name === ':class') {
- expression = attr.value
- }
- }
- if (!expression) return
- attr.value = expression.replace(/'(.+?)'/g, function (match, className) {
- return "'" + convert(className) + "'"
- })
- })
- })
- return parse5.serialize(tree)
-}
diff --git a/lib/loader.js b/lib/loader.js
index e4a560799..0c5d49325 100644
--- a/lib/loader.js
+++ b/lib/loader.js
@@ -208,7 +208,6 @@ module.exports = function (content) {
// add require for template
var template
- var rewriter = 'require("' + require.resolve('./htmlCssModuleRewriter') + '")'
if (parts.template.length) {
template = parts.template[0]
output += '__vue_template__ = ' + (
@@ -216,8 +215,6 @@ module.exports = function (content) {
? getRequireForImport('template', template, hasLocalStyles)
: getRequire('template', template, 0, hasLocalStyles)
)
- output += 'console.warn(__vue_template__)\n'
- output += '__vue_template__ = ' + rewriter + '(__vue_template__, __vue_styles__)\n'
}
if (!query.inject) {
@@ -231,13 +228,12 @@ module.exports = function (content) {
'if (__vue_template__) {\n' +
'__vue_options__.template = __vue_template__\n' +
'}\n' +
- 'if (__vue_options__.loader) {\n' +
- 'var $styles = __vue_options__.loader.styles\n' +
- 'if ($styles) {\n' +
- '(__vue_options__.computed || (__vue_options__.computed = {}))[$styles] = ' +
- 'function () { return __vue_styles__ }\n' +
- '}\n' +
- '}\n'
+ // inject style modules as computed properties
+ 'if (!__vue_options__.computed) __vue_options__.computed = {}\n' +
+ 'Object.keys(__vue_styles__).forEach(function (key) {\n' +
+ 'var module = __vue_styles__[key]\n' +
+ '__vue_options__.computed[key] = function () { return module }\n' +
+ '})\n'
// hot reload
if (
!this.minimize &&
From b4512fa28cc40efe22995ff3e8ee65e84bde1e40 Mon Sep 17 00:00:00 2001
From: YiSiWang <597222964@qq.com>
Date: Wed, 31 Aug 2016 12:14:55 +0800
Subject: [PATCH 11/16] update README
---
README.md | 84 +++++++++----------------------------------------------
1 file changed, 13 insertions(+), 71 deletions(-)
diff --git a/README.md b/README.md
index b46f84d33..a05755b8c 100644
--- a/README.md
+++ b/README.md
@@ -1,89 +1,31 @@
# Add CSS modules support
-## Basic
+Simply use CSS Modules with `
-
-
-
-```
-
-becomes:
+Example:
```html
-
-
-
-```
-
-Class names are unique. So your style won't affect any other component.
-
-Tips:
-
-1. With `module`, animation names are also converted. Feel free to write short class & animation names!
-2. With `module` + `scoped`, you can limit your style to your component more strictly.
-3. With `module` only, you can style `` in your component.
-
-## Binding classes
-
-*Static* class names in binding expression are converted, too.
-```html
-
-
-
-
-
-
-```
-
-becomes:
-
-```html
-
-
-
-
-
-
-```
-
-Note: Here, "static class name" means class name in single quotes.
-
-Complex ones (eg. `'a' + 'b'`) are not supported (and not recommend in Vue).
-
-## Getting local class name in script
-
-In some cases, you will like to control your class name in script.
-You can do it like this:
-
-```html
+
-
+
```
From 90145a4eeda504c5c05f941748e79a5e93c59435 Mon Sep 17 00:00:00 2001
From: YiSiWang <597222964@qq.com>
Date: Wed, 31 Aug 2016 12:23:52 +0800
Subject: [PATCH 12/16] remove unused codes
---
lib/loader.js | 12 ++++--------
1 file changed, 4 insertions(+), 8 deletions(-)
diff --git a/lib/loader.js b/lib/loader.js
index 0c5d49325..87ff62459 100644
--- a/lib/loader.js
+++ b/lib/loader.js
@@ -156,14 +156,10 @@ module.exports = function (content) {
}
var cssModules = {}
- function setCssModule (style, require, context) {
+ function setCssModule (style, require) {
if (!style.module) return require
- if (!(/^[a-zA-Z][a-zA-Z0-9]*$/.test(style.module))) {
- context.emitError('Invallid CSS module name "' + style.module + '"!')
- return require
- }
if (style.module in cssModules) {
- context.emitError('CSS module name "' + style.module + '" is not unique!')
+ loaderContext.emitError('CSS module name "' + style.module + '" is not unique!')
return require
}
cssModules[style.module] = true
@@ -173,13 +169,13 @@ module.exports = function (content) {
// add requires for src imports
parts.styleImports.forEach(function (impt) {
if (impt.scoped) hasLocalStyles = true
- output += setCssModule(impt, getRequireForImport('style', impt, impt.scoped, impt.module), loaderContext) || ''
+ output += setCssModule(impt, getRequireForImport('style', impt, impt.scoped, impt.module)) || ''
})
// add requires for styles
parts.style.forEach(function (style, i) {
if (style.scoped) hasLocalStyles = true
- output += setCssModule(style, getRequire('style', style, i, style.scoped, style.module), loaderContext) || ''
+ output += setCssModule(style, getRequire('style', style, i, style.scoped, style.module)) || ''
})
// add require for script
From da61470fd1d014a2a383b4240b3e16a07c50ca34 Mon Sep 17 00:00:00 2001
From: YiSiWang <597222964@qq.com>
Date: Fri, 2 Sep 2016 15:04:06 +0800
Subject: [PATCH 13/16] add support for preprocessor
---
lib/loader.js | 59 ++++++++++++++++++++++++-----------
test/fixtures/css-modules.vue | 5 +++
test/test.js | 9 +++++-
3 files changed, 54 insertions(+), 19 deletions(-)
diff --git a/lib/loader.js b/lib/loader.js
index 87ff62459..d2a97b0c8 100644
--- a/lib/loader.js
+++ b/lib/loader.js
@@ -22,7 +22,7 @@ var templateLoader = require.resolve('./template-loader')
module.exports = function (content) {
var defaultLoaders = {
html: 'vue-html-loader',
- css: 'vue-style-loader!css-loader?-sourceMap',
+ css: 'vue-style-loader!css-loader',
js: 'babel-loader?presets[]=es2015&plugins[]=transform-runtime&comments=false'
}
@@ -46,25 +46,25 @@ module.exports = function (content) {
options.cssSourceMap !== false &&
process.env.NODE_ENV !== 'production'
) {
- defaultLoaders.css = 'vue-style-loader!css-loader?+sourceMap'
+ defaultLoaders.css = 'vue-style-loader!css-loader?sourceMap'
}
// check if there are custom loaders specified via
// webpack config, otherwise use defaults
var loaders = assign({}, defaultLoaders, options.loaders)
- function getRequire (type, part, index, scoped, module) {
+ function getRequire (type, part, index, scoped) {
return 'require(' +
- getRequireString(type, part, index, scoped, module) +
+ getRequireString(type, part, index, scoped) +
')\n'
}
- function getRequireString (type, part, index, scoped, module) {
+ function getRequireString (type, part, index, scoped) {
return loaderUtils.stringifyRequest(loaderContext,
// disable all configuration loaders
'!!' +
// get loader string for pre-processors
- getLoaderString(type, part, scoped, module) +
+ getLoaderString(type, part, index, scoped) +
// select the corresponding part from the vue file
getSelectorString(type, index || 0) +
// the url to the actual vuefile
@@ -72,28 +72,48 @@ module.exports = function (content) {
)
}
- function getRequireForImport (type, impt, scoped, module) {
+ function getRequireForImport (type, impt, scoped) {
return 'require(' +
- getRequireForImportString(type, impt, scoped, module) +
+ getRequireForImportString(type, impt, scoped) +
')\n'
}
- function getRequireForImportString (type, impt, scoped, module) {
+ function getRequireForImportString (type, impt, scoped) {
return loaderUtils.stringifyRequest(loaderContext,
'!!' +
- getLoaderString(type, impt, scoped, module) +
+ getLoaderString(type, impt, -1, scoped) +
impt.src
)
}
- function getLoaderString (type, part, scoped, module) {
+ function addCssModulesToLoader (loader, part, index) {
+ if (!part.module) return loader
+ return loader.replace(/((?:^|!)css(?:-loader)?)(\?[^!]*)?/, function (m, $1, $2) {
+ // $1: !css-loader
+ // $2: ?a=b
+ var option = loaderUtils.parseQuery($2)
+ option.modules = true
+ option.importLoaders = true
+ option.localIdentName = '[hash:base64]'
+ if (index !== -1) {
+ // Note:
+ // Class name is generated according to its filename.
+ // Different
+
+
diff --git a/test/test.js b/test/test.js
index a085e1f43..2f638e665 100644
--- a/test/test.js
+++ b/test/test.js
@@ -288,7 +288,9 @@ describe('vue-loader', function () {
expect(className).to.match(/^_/)
// class name in style
- var style = window.document.querySelector('style').textContent
+ var style = [].slice.call(window.document.querySelectorAll('style')).map(function (style) {
+ return style.textContent
+ }).join('\n')
expect(style).to.contain('.' + className + ' {\n color: red;\n}')
// animation name
@@ -298,6 +300,11 @@ describe('vue-loader', function () {
expect(animationName).to.not.equal('fade')
expect(style).to.contain('animation: ' + animationName + ' 1s;')
+ // module + pre-processor + scoped
+ var anotherClassName = module.computed.combined().red
+ expect(anotherClassName).to.match(/^_/).and.not.equal(className)
+ expect(style).to.contain('.' + anotherClassName + '[') // scoped
+
done()
})
})
From 773b0c647eea88d9e67c83b55baaf7596cc56680 Mon Sep 17 00:00:00 2001
From: YiSiWang <597222964@qq.com>
Date: Fri, 2 Sep 2016 15:46:22 +0800
Subject: [PATCH 14/16] improve test case
---
test/test.js | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/test/test.js b/test/test.js
index 2f638e665..8e3cbb096 100644
--- a/test/test.js
+++ b/test/test.js
@@ -294,7 +294,7 @@ describe('vue-loader', function () {
expect(style).to.contain('.' + className + ' {\n color: red;\n}')
// animation name
- var match = style.match(/@-webkit-keyframes\s+(\S+)\s+{/)
+ var match = style.match(/@keyframes\s+(\S+)\s+{/)
expect(match).to.have.length(2)
var animationName = match[1]
expect(animationName).to.not.equal('fade')
@@ -303,7 +303,8 @@ describe('vue-loader', function () {
// module + pre-processor + scoped
var anotherClassName = module.computed.combined().red
expect(anotherClassName).to.match(/^_/).and.not.equal(className)
- expect(style).to.contain('.' + anotherClassName + '[') // scoped
+ var id = '_v-' + hash(require.resolve('./fixtures/css-modules.vue'))
+ expect(style).to.contain('.' + anotherClassName + '[' + id + ']')
done()
})
From f52d96a785ca7794f71f7cb5ac2eeb10271f138a Mon Sep 17 00:00:00 2001
From: YiSiWang <597222964@qq.com>
Date: Thu, 8 Sep 2016 19:02:05 +0800
Subject: [PATCH 15/16] add default module support
---
lib/loader.js | 2 ++
lib/parser.js | 2 +-
test/fixtures/css-modules.vue | 2 +-
test/test.js | 4 ++--
4 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/lib/loader.js b/lib/loader.js
index d2a97b0c8..b41a09a00 100644
--- a/lib/loader.js
+++ b/lib/loader.js
@@ -190,6 +190,7 @@ module.exports = function (content) {
// add requires for src imports
parts.styleImports.forEach(function (impt) {
if (impt.scoped) hasLocalStyles = true
+ if (impt.module === '') impt.module = '$style'
var requireString = getRequireForImport('style', impt, impt.scoped, impt.module)
output += setCssModule(impt, requireString)
})
@@ -197,6 +198,7 @@ module.exports = function (content) {
// add requires for styles
parts.style.forEach(function (style, i) {
if (style.scoped) hasLocalStyles = true
+ if (style.module === '') style.module = '$style'
var requireString = getRequire('style', style, i, style.scoped, style.module)
output += setCssModule(style, requireString)
})
diff --git a/lib/parser.js b/lib/parser.js
index 1d338771f..03d4514b6 100644
--- a/lib/parser.js
+++ b/lib/parser.js
@@ -40,7 +40,7 @@ module.exports = function (content, filename, needMap) {
var lang = getAttribute(node, 'lang')
var src = getAttribute(node, 'src')
var scoped = getAttribute(node, 'scoped') != null
- var module = getAttribute(node, 'module') || ''
+ var module = getAttribute(node, 'module')
var warnings = null
var map = null
diff --git a/test/fixtures/css-modules.vue b/test/fixtures/css-modules.vue
index da2f9b4bc..8646cb868 100644
--- a/test/fixtures/css-modules.vue
+++ b/test/fixtures/css-modules.vue
@@ -10,7 +10,7 @@
}
-
diff --git a/test/test.js b/test/test.js
index 8e3cbb096..2abbc0580 100644
--- a/test/test.js
+++ b/test/test.js
@@ -300,8 +300,8 @@ describe('vue-loader', function () {
expect(animationName).to.not.equal('fade')
expect(style).to.contain('animation: ' + animationName + ' 1s;')
- // module + pre-processor + scoped
- var anotherClassName = module.computed.combined().red
+ // default module + pre-processor + scoped
+ var anotherClassName = module.computed.$style().red
expect(anotherClassName).to.match(/^_/).and.not.equal(className)
var id = '_v-' + hash(require.resolve('./fixtures/css-modules.vue'))
expect(style).to.contain('.' + anotherClassName + '[' + id + ']')
From b9fb1c8f5da1a72289592d91cf2d507098775281 Mon Sep 17 00:00:00 2001
From: YiSiWang <597222964@qq.com>
Date: Wed, 14 Sep 2016 11:54:39 +0800
Subject: [PATCH 16/16] update docs
---
README.md | 33 -------------------
docs/en/SUMMARY.md | 1 +
docs/en/features/css-modules.md | 56 +++++++++++++++++++++++++++++++++
docs/en/features/scoped-css.md | 2 +-
4 files changed, 58 insertions(+), 34 deletions(-)
create mode 100644 docs/en/features/css-modules.md
diff --git a/README.md b/README.md
index a05755b8c..a4178a27d 100644
--- a/README.md
+++ b/README.md
@@ -1,36 +1,3 @@
-# Add CSS modules support
-
-Simply use CSS Modules with `
-
-
-
-
-
-
-```
-
-# Original README below:
-
# vue-loader [](https://circleci.com/gh/vuejs/vue-loader/tree/master) [](https://www.npmjs.com/package/vue-loader)
> Vue.js component loader for [Webpack](http://webpack.github.io).
diff --git a/docs/en/SUMMARY.md b/docs/en/SUMMARY.md
index 1c4d10f64..c8ed4b7a7 100644
--- a/docs/en/SUMMARY.md
+++ b/docs/en/SUMMARY.md
@@ -4,6 +4,7 @@
- Features
- [ES2015 and Babel](features/es2015.md)
- [Scoped CSS](features/scoped-css.md)
+ - [CSS Modules](features/css-modules.md)
- [PostCSS and Autoprefixer](features/postcss.md)
- [Hot Reload](features/hot-reload.md)
- Configurations
diff --git a/docs/en/features/css-modules.md b/docs/en/features/css-modules.md
new file mode 100644
index 000000000..b69b74294
--- /dev/null
+++ b/docs/en/features/css-modules.md
@@ -0,0 +1,56 @@
+# CSS Modules
+
+[CSS Modules](https://github.com/css-modules/css-modules) aims to solve class & animation name conflicts. It replaces all the local names with unique hashes and provides a name-to-hash map. So you can write short and general names without worrying any conflict!
+
+With vue-loader, you can simply use CSS Modules with `
+
+
+
+
+
+
+```
+
+If you need mutiple `
+
+
+
+
+
+
+```
+
+## Tips
+
+1. Animation names also get transformed. So, it's recommended to use animations with CSS modules.
+
+2. You can use `scoped` and `module` together to avoid problems in descendant selectors.
+
+3. Use `module` only (without `scoped`), you are able to style ``s and children components. But styling children components breaks the principle of components. You can put `` in a classed wrapper and style it under that class.
+
+4. You can expose the class name of component's root element for theming.
diff --git a/docs/en/features/scoped-css.md b/docs/en/features/scoped-css.md
index 4a1f19709..f2bb9d1e9 100644
--- a/docs/en/features/scoped-css.md
+++ b/docs/en/features/scoped-css.md
@@ -48,4 +48,4 @@ Into the following:
4. **Scoped styles do not eliminate the need for classes**. Due to the way browsers render various CSS selectors, `p { color: red }` will be many times slower when scoped (i.e. when combined with an attribute selector). If you use classes or ids instead, such as in `.example { color: red }`, then you virtually eliminate that performance hit. [Here's a playground](http://stevesouders.com/efws/css-selectors/csscreate.php) where you can test the differences yourself.
-5. **Be careful with descendant selectors in recursive components!** For a CSS rule with the selector `.a .b`, if the element that matches `.a` contains a recursive child component, then all `.b` in that child component will be matched by the rule.
+5. **Be careful with descendant selectors in recursive components!** For a CSS rule with the selector `.a .b`, if the element that matches `.a` contains a recursive child component, then all `.b` in that child component will be matched by the rule. To avoid class name conflicts, you can use [CSS Modules](features/css-modules.md).