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

Commit

Permalink
feat: support for functional template compilation (#45)
Browse files Browse the repository at this point in the history
fixes #44 functional template support
fixes #37 using prettier instead of js_beautify
  • Loading branch information
znck authored Jan 13, 2018
1 parent 72ff0bb commit 63b91f7
Show file tree
Hide file tree
Showing 10 changed files with 130 additions and 15 deletions.
11 changes: 8 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,11 @@
"postcss": "^6.0.12",
"postcss-modules-sync": "^1.0.0",
"postcss-selector-parser": "^2.2.3",
"prettier": "^1.10.2",
"source-map": "^0.6.1",
"superstruct": "^0.5.0",
"vue-hot-reload-api": "^2.1.0",
"vue-template-compiler": "^2.5.13",
"vue-template-es2015-compiler": "^1.5.3"
"vue-template-es2015-compiler": "^1.6.0"
}
}
5 changes: 5 additions & 0 deletions src/assemble.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,10 @@ module.exports = function assemble (source, filename, config) {
})
}

// template functional
output += '\n/* template functional */\n'
output += 'var __vue_template_functional__ = ' + (render.descriptor && render.descriptor.attrs && render.descriptor.attrs.functional ? 'true' : 'false') + '\n'

// style
output += '\n/* styles */\n'
output += 'var __vue_styles__ = ' + (styles.length ? STYLE_IDENTIFIER : 'null') + '\n'
Expand All @@ -175,6 +179,7 @@ module.exports = function assemble (source, filename, config) {
output += `\nvar ${COMPONENT_IDENTIFIER} = ${NORMALIZE_COMPONENT_IDENTIFIER}(\n` +
' __vue_script__,\n' +
' __vue_template__,\n' +
' __vue_template_functional__,\n' +
' __vue_styles__,\n' +
' __vue_scopeId__,\n' +
' __vue_module_identifier__\n' +
Expand Down
9 changes: 9 additions & 0 deletions src/runtime/normalize-component.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
export default function normalizeComponent (
rawScriptExports,
compiledTemplate,
isFunctionalTemplate,
injectStyles,
scopeId,
moduleIdentifier /* server only */
Expand All @@ -28,6 +29,11 @@ export default function normalizeComponent (
if (compiledTemplate) {
options.render = compiledTemplate.render
options.staticRenderFns = compiledTemplate.staticRenderFns
options._compiled = true
}

if (isFunctionalTemplate) {
options.functional = true
}

// scopedId
Expand Down Expand Up @@ -74,6 +80,9 @@ export default function normalizeComponent (
? [].concat(existing, hook)
: [hook]
} else {
// for template-only hot-reload because in that case the render fn doesn't
// go through the normalizer
options._injectStyles = hook
// register for functioal component in vue file
options.render = function renderWithStyleInjection (h, context) {
hook.call(context)
Expand Down
21 changes: 13 additions & 8 deletions src/template-compiler/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const compiler = require('vue-template-compiler/build.js')
const transpile = require('vue-template-es2015-compiler')
const { js_beautify: beautify } = require('js-beautify')
const prettier = require('prettier')
const { struct } = require('superstruct')

const transformRequire = require('./modules/transform-require')
Expand Down Expand Up @@ -30,7 +30,8 @@ const Config = struct({
optimizeSSR: true,
buble: {
transforms: {
stripWith: true
stripWith: true,
stripWithFunctional: false
}
},
options: {
Expand Down Expand Up @@ -61,9 +62,13 @@ module.exports = function compileTemplate (template, filename, config) {
if (output.errors && output.errors.length) {
output.code = `function render () {}\nvar staticRenderFns = []`
} else {
const stripWithFunctional = template.descriptor.attrs && template.descriptor.attrs.functional

config.buble.transforms.stripWithFunctional = stripWithFunctional

output.code = transpile(
'var render = ' + toFunction(compiled.render) + '\n' +
'var staticRenderFns = [' + compiled.staticRenderFns.map(toFunction).join(',') + ']',
'var render = ' + toFunction(compiled.render, stripWithFunctional) + '\n' +
'var staticRenderFns = [' + compiled.staticRenderFns.map(it => toFunction(it, stripWithFunctional)).join(',') + ']',
config.buble
)

Expand All @@ -85,8 +90,8 @@ module.exports = function compileTemplate (template, filename, config) {
return output
}

function toFunction (code) {
return 'function () {' + beautify(code, {
indent_size: 2 // eslint-disable-line camelcase
}) + '}'
function toFunction (code, stripWithFunctional) {
return `function (${stripWithFunctional ? '_h,_vm' : ''}) {\n ${
prettier.format(code).split(/\r?\n/).map(it => ' ' + it).join('\n')
}\n}`
}
28 changes: 28 additions & 0 deletions test/assemble.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { mount } from '@vue/test-utils'

// Fixtures
import Transform from './fixtures/transform.vue'
import Functional from './fixtures/functional.vue'
import FunctionalRoot from './fixtures/functional-root.vue'

test('transfom to require', () => {
const wrapper = mount(Transform)
Expand All @@ -26,3 +28,29 @@ test('transform srcset', () => {

expect(wrapper.element.children[2].children[0].getAttribute('xlink:href')).toEqual(data)
})

test('functional template', () => {
expect(Functional._compiled).toEqual(true)
expect(Functional.functional).toEqual(true)
expect(Functional.staticRenderFns).toBeTruthy()
expect(Functional.render.length).toEqual(2)

FunctionalRoot.methods = {} // Bug in vue-test-utils
const wrapper = mount(FunctionalRoot)
const div = wrapper.element.children[0]
const fn = jest.fn()

wrapper.setMethods({ fn })

expect(wrapper.find('h2').element.style.color).toEqual('red')
expect(wrapper.find('span').text()).toEqual('hello')

expect(div.children[2].textContent).toEqual('Second slot')
expect(div.children[2].nextSibling.textContent).toEqual(' hello ')

expect(div.children[3].textContent).toEqual('Some text')
expect(div.children[3].children[0].textContent).toEqual('text')

wrapper.find('.clickable').trigger('click')
expect(fn).toHaveBeenCalled()
})
18 changes: 18 additions & 0 deletions test/fixtures/functional-root.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<template>
<div>
<functional>
<span>hello</span>
<div slot="slot2">Second slot</div>
<template slot="scoped" scope="scope">{{ scope.msg }}</template>
</functional>
</div>
</template>

<script>
import Functional from './functional.vue'
export default {
components: {
Functional
}
}
</script>
12 changes: 12 additions & 0 deletions test/fixtures/functional-style.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<script>
export default {
functional: true,
render (h) {
return h('div', { class: 'foo', attrs: { id: 'test' } }, ['Hello functional'])
}
}
</script>

<style>
.foo { color: red; }
</style>
22 changes: 22 additions & 0 deletions test/fixtures/functional.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<template functional>
<div>
<h2 id="test" style="color: red">Hello! {{ props.msg }}</h2>
<slot></slot>
<slot name="slot2"></slot>
<slot :msg="props.msg" name="scoped"></slot>
<div>Some <span>text</span></div>
<div v-if="false">Not exist</div>
<div class="clickable" @click="parent.fn"></div>
</div>
</template>

<script>
export default {
props: {
msg: {
type: String,
default: 'hello'
}
}
}
</script>
16 changes: 13 additions & 3 deletions test/setup/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@ const compiler = require('../..')

module.exports = { compile, build, open, pack }

function vue () {
return {
name: 'vue',
transform (code, id) {
if (id.endsWith('.vue')) return compile(id, code)
}
}
}

function inline (filename, code) {
return {
name: 'Inline',
Expand Down Expand Up @@ -75,7 +84,7 @@ async function pack (filename, source) {
let bundle = await rollup.rollup({
input: name,
plugins: [
inline(name, compile(filename, source)), babelit
inline(name, compile(filename, source)), vue(), babelit
]
})
bundle = await rollup.rollup({
Expand All @@ -100,6 +109,7 @@ async function build (filename) {
let bundle = await rollup.rollup({
input,
plugins: [
vue(),
image(),
nodeResolve(),
inline(component, source),
Expand All @@ -124,7 +134,7 @@ async function build (filename) {
bundle = await rollup.rollup({
input: component,
plugins: [
image(), commonjs(), inline(component, generated.code), babelit
vue(), image(), commonjs(), inline(component, generated.code), babelit
]
})

Expand All @@ -145,7 +155,7 @@ async function open (browser, code, id = '#test') {
<!doctype html>
<html>
<head>
<title>Test: ${it}</title>
<title>Test</title>
</head>
<body>
<div id="app"></div>
Expand Down

0 comments on commit 63b91f7

Please sign in to comment.