Skip to content
This repository has been archived by the owner on Aug 16, 2022. It is now read-only.

Commit

Permalink
feat: template to render function compiler
Browse files Browse the repository at this point in the history
  • Loading branch information
znck committed Oct 3, 2017
1 parent 6b9dc62 commit c4fc825
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 0 deletions.
68 changes: 68 additions & 0 deletions src/template-compiler/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
const compiler = require('vue-template-compiler')
const transpile = require('vue-template-es2015-compiler')
const { js_beautify: beautify } = require('js-beautify')

const transformRequire = require('./modules/transform-require')

module.exports = function compileTemplate (template, filename, config) {
const options = Object.assign({
preserveWhitespace: true
}, config.options, {
modules: [transformRequire(config.transformToRequire)].concat(config.plugins || [])
})
const compile = (config.isServer && config.optimizeSSR !== false && compiler.ssrCompile) ? compiler.ssrCompile : compiler.compile
const compiled = compile(template.code, options)
const output = {
errors: compiled.errors,
tips: compiled.tips
}

if (output.errors && output.errors.length) {
output.code = config.esModule !== false
? `export function render () {}\nexport var staticRenderFns = []`
: 'module.exports={render:function(){},staticRenderFns:[]}'
} else {
output.code = transpile(
'var render = ' + toFunction(compiled.render) + '\n' +
'var staticRenderFns = [' + compiled.staticRenderFns.map(toFunction).join(',') + ']',
config.buble
) + '\n'

// mark with stripped (this enables Vue to use correct runtime proxy detection)
if (
!config.isProduction && (
!config.buble ||
!config.buble.transforms ||
config.buble.transforms.stripWith !== false
)) {
output.code += `render._withStripped = true\n`
}

const __exports__ = `{ render: render, staticRenderFns: staticRenderFns }`
output.code += config.esModule !== false
? `export default ${__exports__}`
: `module.exports = ${__exports__}`

if (!config.isProduction && config.isHot) {
output.code +=
'\nif (module.hot) {\n' +
' module.hot.accept()\n' +
' if (module.hot.data) {\n' +
` require('vue-hot-reload-api').rerender('${options.scopeId}', module.exports)\n` +
' }\n' +
'}'
}
}

return Promise.resolve(output)
}

function toFunction (code) {
return 'function () {' + beautify(code, {
indent_size: 2 // eslint-disable-line camelcase
}) + '}'
}

function pad (html) {
return html.split(/\r?\n/).map(line => ` ${line}`).join('\n')
}
48 changes: 48 additions & 0 deletions src/template-compiler/modules/transform-require.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// vue compiler module for transforming `<tag>:<attribute>` to `require`
const defaultOptions = {
img: 'src',
image: 'xlink:href'
}

module.exports = userOptions => {
const options = userOptions
? Object.assign({}, defaultOptions, userOptions)
: defaultOptions

return {
postTransformNode: node => {
transform(node, options)
}
}
}

function transform (node, options) {
for (const tag in options) {
if (node.tag === tag && node.attrs) {
const attributes = options[tag]
if (typeof attributes === 'string') {
node.attrs.some(attr => rewrite(attr, attributes))
} else if (Array.isArray(attributes)) {
attributes.forEach(item => node.attrs.some(attr => rewrite(attr, item)))
}
}
}
}

function rewrite (attr, name) {
if (attr.name === name) {
let value = attr.value
const isStatic = value.charAt(0) === '"' && value.charAt(value.length - 1) === '"'
if (!isStatic) {
return
}
const firstChar = value.charAt(1)
if (firstChar === '.' || firstChar === '~') {
if (firstChar === '~') {
value = '"' + value.slice(2)
}
attr.value = `require(${value})`
}
return true
}
}
32 changes: 32 additions & 0 deletions test/template-compiler.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
const compiler = require('../src/template-compiler')

test('should compile template to esModule', async () => {
const template = {
code: '<div>{{foo}}</div>\n'
}
const compiled = await compiler(template, 'foo.vue', { scopeId: 'xxx' })

expect(compiled.code.indexOf('export default')).toBeGreaterThan(-1)
expect(compiled.code.indexOf('render._withStripped')).toBeGreaterThan(-1)
expect(compiled.code.indexOf('module.hot.accept')).toBe(-1)
})

test('should compile template to node module', async () => {
const template = {
code: '<div>{{foo}}</div>\n'
}
const compiled = await compiler(template, 'foo.vue', { scopeId: 'xxx', esModule: false })

expect(compiled.code.indexOf('export default')).toBe(-1)
expect(compiled.code.indexOf('render._withStripped')).toBeGreaterThan(-1)
expect(compiled.code.indexOf('module.hot.accept')).toBe(-1)
})

test('should compile with HMR', async () => {
const template = {
code: '<div>{{foo}}</div>\n'
}
const compiled = await compiler(template, 'foo.vue', { scopeId: 'xxx', isHot: true })

expect(compiled.code.indexOf('module.hot.accept')).toBeGreaterThan(-1)
})

0 comments on commit c4fc825

Please sign in to comment.