Commit 5d0c4f7b authored by Evan You's avatar Evan You

test: css modules

parent 99754c01
......@@ -10,8 +10,8 @@ module.exports = code => code
module.exports.pitch = function (remainingRequest) {
const query = qs.parse(this.resourceQuery.slice(1))
if (query.vue != null) {
// For Scoped CSS: inject style-post-loader before css-loader
if (query.type === `style` && query.scoped != null) {
// Inject style-post-loader before css-loader for scoped CSS and trimming
if (query.type === `style`) {
const cssLoaderIndex = this.loaders.findIndex(l => /\/css-loader/.test(l.request))
if (cssLoaderIndex) {
const afterLoaders = this.loaders.slice(1, cssLoaderIndex + 1).map(l => l.request)
......@@ -22,7 +22,8 @@ module.exports.pitch = function (remainingRequest) {
...beforeLoaders,
this.resourcePath + this.resourceQuery
].join('!')
return `export * from ${loaderUtils.stringifyRequest(this, request)}`
// use cjs to ensure exports from (vue-)style-loader/css-loader are intact
return `module.exports = require(${loaderUtils.stringifyRequest(this, request)})`
}
}
......@@ -34,6 +35,7 @@ module.exports.pitch = function (remainingRequest) {
...beforeLoaders,
this.resourcePath + this.resourceQuery
].join('!')
// the template compiler uses esm exports
return `export * from ${loaderUtils.stringifyRequest(this, request)}`
}
}
......
......@@ -11,7 +11,11 @@ module.exports = function (source, map) {
const query = qs.parse(this.resourceQuery.slice(1))
const id = `data-v-${query.id}`
const plugins = [trim(), scoped({ id })]
const plugins = [trim()]
if (query.scoped != null) {
plugins.push(scoped({ id }))
}
const options = {
to: this.resourcePath,
......
......@@ -75,7 +75,7 @@ test('style import', done => {
entry: 'style-import.vue'
}, ({ window }) => {
const styles = window.document.querySelectorAll('style')
expect(styles[0].textContent).toContain('h1 { color: red; }')
expect(styles[0].textContent).toContain('h1 { color: red;\n}')
// import with scoped
const id = 'data-v-' + genId('style-import.vue')
expect(styles[1].textContent).toContain('h1[' + id + '] { color: green;\n}')
......@@ -90,7 +90,7 @@ test('style import for a same file twice', done => {
initStylesForAllSubComponents(module)
const styles = window.document.querySelectorAll('style')
expect(styles.length).toBe(3)
expect(styles[0].textContent).toContain('h1 { color: red; }')
expect(styles[0].textContent).toContain('h1 { color: red;\n}')
// import with scoped
const id = 'data-v-' + genId('style-import-twice-sub.vue')
expect(styles[1].textContent).toContain('h1[' + id + '] { color: green;\n}')
......
......@@ -10,11 +10,11 @@
}
</style>
<style scoped lang="stylus" module>
<style lang="stylus" module scoped>
.red
color: red
</style>
<script>
module.exports = {}
export default {}
</script>
......@@ -106,49 +106,79 @@ test('postcss', done => {
})
})
// TODO
// test('css-modules', done => {
// function testWithIdent (localIdentName, regexToMatch, cb) {
// mockBundleAndRun({
// entry: 'css-modules.vue',
// vue: {
// cssModules: localIdentName && {
// localIdentName: localIdentName
// }
// }
// }, (window, module, raw, instance) => {
// // get local class name
// const className = instance.style.red
// expect(className).toMatch(regexToMatch)
test('css-modules', async () => {
function testWithIdent (localIdentName, regexToMatch) {
return new Promise((resolve, reject) => {
const baseLoaders = [
'vue-style-loader',
{
loader: 'css-loader',
options: {
modules: true,
localIdentName
}
}
]
mockBundleAndRun({
entry: 'css-modules.vue',
modify: config => {
config.module.rules = [
{
test: /\.vue$/,
loader: 'vue-loader'
},
{
test: /\.css$/,
use: baseLoaders
},
{
test: /\.stylus$/,
use: [
...baseLoaders,
'stylus-loader'
]
}
]
}
}, ({ window, instance, jsdomError, bundleError }) => {
if (jsdomError) return reject(jsdomError)
if (bundleError) return reject(bundleError)
// // class name in style
// let style = [].slice.call(window.document.querySelectorAll('style')).map((style) => {
// return style.textContent
// }).join('\n')
// style = normalizeNewline(style)
// expect(style).toContain('.' + className + ' {\n color: red;\n}')
// get local class name
const className = instance.style.red
expect(className).toMatch(regexToMatch)
// // animation name
// const match = style.match(/@keyframes\s+(\S+)\s+{/)
// expect(match).toHaveLength(2)
// const animationName = match[1]
// expect(animationName).not.toBe('fade')
// expect(style).toContain('animation: ' + animationName + ' 1s;')
// class name in style
let style = [].slice.call(window.document.querySelectorAll('style')).map((style) => {
return style.textContent
}).join('\n')
style = normalizeNewline(style)
expect(style).toContain('.' + className + ' {\n color: red;\n}')
// // default module + pre-processor + scoped
// const anotherClassName = instance.$style.red
// expect(anotherClassName).to.match(regexToMatch).not.toBe(className)
// const id = 'data-v-' + genId('css-modules.vue')
// expect(style).toContain('.' + anotherClassName + '[' + id + ']')
// animation name
const match = style.match(/@keyframes\s+(\S+)\s+{/)
expect(match).toHaveLength(2)
const animationName = match[1]
expect(animationName).not.toBe('fade')
expect(style).toContain('animation: ' + animationName + ' 1s;')
// cb()
// })
// }
// // default localIdentName
// testWithIdent(undefined, /^red_\w{8}/, () => {
// // specified localIdentName
// const ident = '[path][name]---[local]---[hash:base64:5]'
// const regex = /css-modules---red---\w{5}/
// testWithIdent(ident, regex, done)
// })
// })
// default module + pre-processor + scoped
const anotherClassName = instance.$style.red
expect(anotherClassName).toMatch(regexToMatch)
const id = 'data-v-' + genId('css-modules.vue')
expect(style).toContain('.' + anotherClassName + '[' + id + ']')
resolve()
})
})
}
// default ident
await testWithIdent(undefined, /^\w{22}/)
// custom ident
await testWithIdent(
'[path][name]---[local]---[hash:base64:5]',
/css-modules---red---\w{5}/
)
})
const path = require('path')
const normalizeNewline = require('normalize-newline')
const {
mockRender,
mockBundleAndRun
......@@ -63,8 +64,8 @@ test('transform relative URLs and respects resolve alias', done => {
expect(vnode.children[2].data.attrs.src).toBe('logo.c9e00e.png')
const style = window.document.querySelector('style').textContent
expect(style).toContain('html { background-image: url(logo.c9e00e.png); }')
expect(style).toContain('body { background-image: url(logo.c9e00e.png); }')
expect(style).toContain('html { background-image: url(logo.c9e00e.png);\n}')
expect(style).toContain('body { background-image: url(logo.c9e00e.png);\n}')
done()
})
})
......@@ -109,24 +110,23 @@ test('transform srcset', done => {
})
})
// TODO
// test('functional component with styles', done => {
// mockBundleAndRun({
// entry: 'functional-style.vue'
// }, ({ window, module, rawModule }) => {
// expect(module.functional).toBe(true)
// const vnode = mockRender(module)
// // <div class="foo">hi</div>
// expect(vnode.tag).toBe('div')
// expect(vnode.data.class).toBe('foo')
// expect(vnode.children[0].text).toBe('functional')
// let style = window.document.querySelector('style').textContent
// style = normalizeNewline(style)
// expect(style).toContain('.foo { color: red;\n}')
// done()
// })
// })
test('functional component with styles', done => {
mockBundleAndRun({
entry: 'functional-style.vue'
}, ({ window, module, rawModule }) => {
expect(module.functional).toBe(true)
const vnode = mockRender(module)
// <div class="foo">hi</div>
expect(vnode.tag).toBe('div')
expect(vnode.data.class).toBe('foo')
expect(vnode.children[0].text).toBe('functional')
let style = window.document.querySelector('style').textContent
style = normalizeNewline(style)
expect(style).toContain('.foo { color: red;\n}')
done()
})
})
test('functional template', done => {
mockBundleAndRun({
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment