Skip to content

Commit c261e0a

Browse files
committed
support babel transpile in template render function
1 parent a8f3468 commit c261e0a

File tree

4 files changed

+126
-36
lines changed

4 files changed

+126
-36
lines changed

lib/loader.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ module.exports = function (content) {
5353
options.cssSourceMap !== false
5454

5555
var defaultLoaders = {
56-
html: templateCompilerPath + '?id=' + moduleId,
56+
html: templateCompilerPath + '?id=' + moduleId + (hasBabel ? '&babel=true' : ''),
5757
css: styleLoaderPath + '!css-loader' + (needCssSourceMap ? '?sourceMap' : ''),
5858
js: hasBabel ? 'babel-loader' : ''
5959
}

lib/template-compiler.js

+34-8
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ function rewrite (attr) {
3535

3636
module.exports = function (html) {
3737
this.cacheable()
38+
var query = loaderUtils.parseQuery(this.query)
3839
var isServer = this.options.target === 'node'
3940
var compiled = compiler.compile(html, options)
4041
var code
@@ -46,28 +47,53 @@ module.exports = function (html) {
4647
code = 'module.exports={render:function(){},staticRenderFns:[]}'
4748
} else {
4849
code = 'module.exports={' +
49-
'render:' + toFunction(compiled.render) + ',' +
50-
'staticRenderFns: [' + compiled.staticRenderFns.map(toFunction).join(',') + ']' +
50+
'render:' + toFunction(compiled.render, query.babel) + ',' +
51+
'staticRenderFns: [' + compiled.staticRenderFns.map(function (code) {
52+
return toFunction(code, query.babel)
53+
}).join(',') + ']' +
5154
'}'
55+
56+
if (query.babel) {
57+
var babelOptions = {
58+
filename: require('path').basename(this.resourcePath),
59+
sourceRoot: process.cwd(),
60+
babelrc: true
61+
}
62+
if (this.options.babel) {
63+
for (var key in this.options.babel) {
64+
babelOptions[key] = this.options.babel[key]
65+
}
66+
}
67+
code = transpile(code, babelOptions)
68+
}
5269
}
5370
// hot-reload
5471
if (!isServer &&
5572
!this.minimize &&
5673
process.env.NODE_ENV !== 'production') {
57-
var moduleId = loaderUtils.parseQuery(this.query).id
5874
code +=
5975
'\nif (module.hot) {\n' +
6076
' module.hot.accept()\n' +
6177
' if (module.hot.data) {\n' +
62-
' require("' + hotReloadAPIPath + '").rerender("' + moduleId + '", module.exports)\n' +
78+
' require("' + hotReloadAPIPath + '").rerender("' + query.id + '", module.exports)\n' +
6379
' }\n' +
6480
'}'
6581
}
6682
return code
6783
}
6884

69-
function toFunction (code) {
70-
return 'function(){' +
71-
beautify(code, { indent_size: 2 }) +
72-
'}'
85+
function transpile (code, options) {
86+
return require('babel-core').transform(code, options).code
87+
// strip use strict
88+
.replace(/['"]use strict['"];?\n?/, '')
89+
// add back with
90+
.replace(/if\s*\("__VUE_LOADER_WITH__"\)/g, 'with(this)')
91+
}
92+
93+
function toFunction (code, needBabel) {
94+
if (needBabel) {
95+
// replace "with(this){" with something that works in strict mode
96+
code = code.replace(/with\(this\)/, 'if("__VUE_LOADER_WITH__")')
97+
}
98+
return 'function (){' + beautify(code, { indent_size: 2 }) + '}'
7399
}

test/fixtures/babel.vue

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<template>
2+
<div :class="{[a]:true}"></div>
3+
</template>

test/test.js

+88-27
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@ var rimraf = require('rimraf')
99
var genId = require('../lib/gen-id')
1010
var SourceMapConsumer = require('source-map').SourceMapConsumer
1111
var ExtractTextPlugin = require("extract-text-webpack-plugin")
12-
var compiler = require('vue-template-compiler')
13-
var beautify = require('js-beautify').js_beautify
12+
var compiler = require('../lib/template-compiler')
1413

1514
var loaderPath = 'expose?vueModule!' + path.resolve(__dirname, '../')
1615
var mfs = new MemoryFS()
@@ -61,11 +60,26 @@ function test (options, assert) {
6160
})
6261
}
6362

64-
function assertRenderFn (options, template) {
65-
var compiled = compiler.compile(template)
66-
expect(options.render.toString().replace(/\t/g, '')).to.equal('function (){' +
67-
beautify(compiled.render, { indent_size: 2 }) +
68-
'}')
63+
function mockRender (options, data) {
64+
return options.render.call(Object.assign({
65+
_h (tag, data, children) {
66+
if (Array.isArray(data)) {
67+
children = data
68+
data = null
69+
}
70+
return {
71+
tag: tag,
72+
data: data,
73+
children: children
74+
}
75+
},
76+
_m (index) {
77+
return options.staticRenderFns[index].call(this)
78+
},
79+
_s (str) {
80+
return String(str)
81+
}
82+
}, data))
6983
}
7084

7185
function interopDefault (module) {
@@ -79,7 +93,14 @@ describe('vue-loader', function () {
7993
test({
8094
entry: './test/fixtures/basic.vue'
8195
}, function (window, module, rawModule) {
82-
assertRenderFn(module, '<h2 class="red">{{msg}}</h2>')
96+
var vnode = mockRender(module, {
97+
msg: 'hi'
98+
})
99+
// <h2 class="red">{{msg}}</h2>
100+
expect(vnode.tag).to.equal('h2')
101+
expect(vnode.data.staticClass).to.equal('red')
102+
expect(vnode.children[0]).to.equal('hi')
103+
83104
expect(module.data().msg).to.contain('Hello from Component A!')
84105
var style = window.document.querySelector('style').textContent
85106
expect(style).to.contain('comp-a h2 {\n color: #f00;\n}')
@@ -91,13 +112,15 @@ describe('vue-loader', function () {
91112
test({
92113
entry: './test/fixtures/pre.vue'
93114
}, function (window, module) {
94-
assertRenderFn(module,
95-
'<div>' +
96-
'<h1>This is the app</h1>' +
97-
'<comp-a></comp-a>' +
98-
'<comp-b></comp-b>' +
99-
'</div>'
100-
)
115+
var vnode = mockRender(module)
116+
// div
117+
// h1 This is the app
118+
// comp-a
119+
// comp-b
120+
expect(vnode.children[0].tag).to.equal('h1')
121+
expect(vnode.children[1].tag).to.equal('comp-a')
122+
expect(vnode.children[2].tag).to.equal('comp-b')
123+
101124
expect(module.data().msg).to.contain('Hello from coffee!')
102125
var style = window.document.querySelector('style').textContent
103126
expect(style).to.contain('body {\n font: 100% Helvetica, sans-serif;\n color: #999;\n}')
@@ -111,14 +134,23 @@ describe('vue-loader', function () {
111134
}, function (window, module) {
112135
var id = 'data-v-' + genId(require.resolve('./fixtures/scoped-css.vue'))
113136
expect(module._scopeId).to.equal(id)
114-
assertRenderFn(module,
115-
'<div>' +
116-
'<div><h1>hi</h1></div>\n' +
117-
'<p class="abc def">hi</p>\n' +
118-
'<template v-if="ok"><p class="test">yo</p></template>\n' +
119-
'<svg><template><p></p></template></svg>' +
120-
'</div>'
121-
)
137+
138+
var vnode = mockRender(module, {
139+
ok: true
140+
})
141+
// <div>
142+
// <div><h1>hi</h1></div>
143+
// <p class="abc def">hi</p>
144+
// <template v-if="ok"><p class="test">yo</p></template>
145+
// <svg><template><p></p></template></svg>
146+
// </div>
147+
expect(vnode.children[0].tag).to.equal('div')
148+
expect(vnode.children[1]).to.equal(' ')
149+
expect(vnode.children[2].tag).to.equal('p')
150+
expect(vnode.children[2].data.staticClass).to.equal('abc def')
151+
expect(vnode.children[4][0].tag).to.equal('p')
152+
expect(vnode.children[4][0].data.staticClass).to.equal('test')
153+
122154
var style = window.document.querySelector('style').textContent
123155
expect(style).to.contain('.test[' + id + '] {\n color: yellow;\n}')
124156
expect(style).to.contain('.test[' + id + ']:after {\n content: \'bye!\';\n}')
@@ -144,7 +176,10 @@ describe('vue-loader', function () {
144176
test({
145177
entry: './test/fixtures/template-import.vue'
146178
}, function (window, module) {
147-
assertRenderFn(module, '<div><h1>hello</h1></div>')
179+
var vnode = mockRender(module)
180+
// '<div><h1>hello</h1></div>'
181+
expect(vnode.children[0].tag).to.equal('h1')
182+
expect(vnode.children[0].children[0]).to.equal('hello')
148183
done()
149184
})
150185
})
@@ -225,8 +260,11 @@ describe('vue-loader', function () {
225260
msg: 'Hello from mocked service!'
226261
}
227262
}))
228-
assertRenderFn(module, '<div class="msg">{{ msg }}</div>')
229-
expect(module.data().msg).to.contain('Hello from mocked service!')
263+
var vnode = mockRender(module, module.data())
264+
// <div class="msg">{{ msg }}</div>
265+
expect(vnode.tag).to.equal('div')
266+
expect(vnode.data.staticClass).to.equal('msg')
267+
expect(vnode.children[0]).to.equal('Hello from mocked service!')
230268
done()
231269
})
232270
})
@@ -246,7 +284,16 @@ describe('vue-loader', function () {
246284
]
247285
}
248286
}, function (window, module) {
249-
assertRenderFn(module, '<img src="logo.c9e00e.png">\n<img src="logo.c9e00e.png">')
287+
var vnode = mockRender(module)
288+
// <div>
289+
// <img src="logo.c9e00e.png">
290+
// <img src="logo.c9e00e.png">
291+
// </div>
292+
expect(vnode.children[0].tag).to.equal('img')
293+
expect(vnode.children[0].data.attrs.src).to.equal('logo.c9e00e.png')
294+
expect(vnode.children[2].tag).to.equal('img')
295+
expect(vnode.children[2].data.attrs.src).to.equal('logo.c9e00e.png')
296+
250297
var style = window.document.querySelector('style').textContent
251298
expect(style).to.contain('html { background-image: url(logo.c9e00e.png);\n}')
252299
expect(style).to.contain('body { background-image: url(logo.c9e00e.png);\n}')
@@ -270,4 +317,18 @@ describe('vue-loader', function () {
270317
done()
271318
})
272319
})
320+
321+
it('transpile template with babel', function (done) {
322+
test({
323+
entry: './test/fixtures/babel.vue'
324+
}, function (window, module) {
325+
var vnode = mockRender(module, {
326+
a: 'hello'
327+
})
328+
// <div :class="{[a]:true}"></div>
329+
expect(vnode.tag).to.equal('div')
330+
expect(vnode.data.class.hello).to.equal(true)
331+
done()
332+
})
333+
})
273334
})

0 commit comments

Comments
 (0)