Browse Source

build: introduce feature flags, hide new slot syntax behind flag

dev
Evan You 6 years ago
parent
commit
7fb6fdd168
  1. 9
      scripts/config.js
  2. 3
      scripts/feature-flags.js
  3. 30
      src/compiler/parser/index.js
  4. 2
      src/core/vdom/helpers/normalize-scoped-slots.js
  5. 250
      test/unit/features/component/component-scoped-slot.spec.js
  6. 4
      test/unit/karma.base.config.js

9
scripts/config.js

@ -7,6 +7,7 @@ const node = require('rollup-plugin-node-resolve')
const flow = require('rollup-plugin-flow-no-whitespace')
const version = process.env.VERSION || require('../package.json').version
const weexVersion = process.env.WEEX_VERSION || require('../packages/weex-vue-framework/package.json').version
const featureFlags = require('./feature-flags')
const banner =
'/*!\n' +
@ -241,9 +242,13 @@ function genConfig (name) {
}
if (opts.env) {
config.plugins.push(replace({
const vars = {
'process.env.NODE_ENV': JSON.stringify(opts.env)
}))
}
Object.keys(featureFlags).forEach(key => {
vars[`process.env.${key}`] = featureFlags[key]
})
config.plugins.push(replace(vars))
}
if (opts.transpile !== false) {

3
scripts/feature-flags.js

@ -0,0 +1,3 @@
module.exports = {
NEW_SLOT_SYNTAX: false
}

30
src/compiler/parser/index.js

@ -568,22 +568,24 @@ function processSlotContent (el) {
}
el.slotScope = (
slotScope ||
getAndRemoveAttr(el, 'slot-scope') ||
getAndRemoveAttr(el, 'slot-scope')
)
if (process.env.NEW_SLOT_SYNTAX) {
// new in 2.6: slot-props and its shorthand works the same as slot-scope
// when used on <template> containers
getAndRemoveAttr(el, 'slot-props')
)
// 2.6 shorthand syntax
const shorthand = getAndRemoveAttrByRegex(el, scopedSlotShorthandRE)
if (shorthand) {
if (process.env.NODE_ENV !== 'production' && el.slotScope) {
warn(
`Unexpected mixed usage of different slot syntaxes.`,
el
)
el.slotScope = el.slotScope || getAndRemoveAttr(el, 'slot-props')
// 2.6 shorthand syntax
const shorthand = getAndRemoveAttrByRegex(el, scopedSlotShorthandRE)
if (shorthand) {
if (process.env.NODE_ENV !== 'production' && el.slotScope) {
warn(
`Unexpected mixed usage of different slot syntaxes.`,
el
)
}
el.slotTarget = getScopedSlotShorthandName(shorthand)
el.slotScope = shorthand.value
}
el.slotTarget = getScopedSlotShorthandName(shorthand)
el.slotScope = shorthand.value
}
} else if ((slotScope = getAndRemoveAttr(el, 'slot-scope'))) {
/* istanbul ignore if */
@ -597,7 +599,7 @@ function processSlotContent (el) {
)
}
el.slotScope = slotScope
} else {
} else if (process.env.NEW_SLOT_SYNTAX) {
// 2.6: slot-props on component, denotes default slot
slotScope = getAndRemoveAttr(el, 'slot-props')
const shorthand = getAndRemoveAttrByRegex(el, scopedSlotShorthandRE)

2
src/core/vdom/helpers/normalize-scoped-slots.js

@ -32,7 +32,7 @@ export function normalizeScopedSlots (
function normalizeScopedSlot(fn: Function): Function {
return scope => {
const res = fn(scope)
return res && typeof res === 'object'
return res && typeof res === 'object' && !Array.isArray(res)
? [res] // single vnode
: normalizeChildren(res)
}

250
test/unit/features/component/component-scoped-slot.spec.js

@ -632,64 +632,116 @@ describe('Component scoped slot', () => {
}).then(done)
})
// new in 2.6
describe('slot-props syntax', () => {
const Foo = {
render(h) {
return h('div', [
this.$scopedSlots.default && this.$scopedSlots.default('from foo default'),
this.$scopedSlots.one && this.$scopedSlots.one('from foo one'),
this.$scopedSlots.two && this.$scopedSlots.two('from foo two')
])
// 2.6 new slot syntax
if (process.env.NEW_SLOT_SYNTAX) {
describe('slot-props syntax', () => {
const Foo = {
render(h) {
return h('div', [
this.$scopedSlots.default && this.$scopedSlots.default('from foo default'),
this.$scopedSlots.one && this.$scopedSlots.one('from foo one'),
this.$scopedSlots.two && this.$scopedSlots.two('from foo two')
])
}
}
}
const Bar = {
render(h) {
return this.$scopedSlots.default && this.$scopedSlots.default('from bar')[0]
const Bar = {
render(h) {
return this.$scopedSlots.default && this.$scopedSlots.default('from bar')
}
}
}
const Baz = {
render(h) {
return this.$scopedSlots.default && this.$scopedSlots.default('from baz')[0]
const Baz = {
render(h) {
return this.$scopedSlots.default && this.$scopedSlots.default('from baz')
}
}
}
function runSuite(syntax) {
it('default slot', () => {
const vm = new Vue({
template: `<foo ${syntax}="foo">{{ foo }}<div>{{ foo }}</div></foo>`,
components: { Foo }
}).$mount()
expect(vm.$el.innerHTML).toBe(`from foo default<div>from foo default</div>`)
})
function runSuite(syntax) {
it('default slot', () => {
const vm = new Vue({
template: `<foo ${syntax}="foo">{{ foo }}<div>{{ foo }}</div></foo>`,
components: { Foo }
}).$mount()
expect(vm.$el.innerHTML).toBe(`from foo default<div>from foo default</div>`)
})
it('nested default slots', () => {
const vm = new Vue({
template: `
<foo ${syntax}="foo">
<bar ${syntax}="bar">
<baz ${syntax}="baz">
{{ foo }} | {{ bar }} | {{ baz }}
</baz>
</bar>
</foo>
`,
components: { Foo, Bar, Baz }
}).$mount()
expect(vm.$el.innerHTML.trim()).toBe(`from foo default | from bar | from baz`)
})
it('nested default slots', () => {
const vm = new Vue({
template: `
<foo ${syntax}="foo">
<bar ${syntax}="bar">
<baz ${syntax}="baz">
{{ foo }} | {{ bar }} | {{ baz }}
</baz>
</bar>
</foo>
`,
components: { Foo, Bar, Baz }
}).$mount()
expect(vm.$el.innerHTML.trim()).toBe(`from foo default | from bar | from baz`)
})
it('default + named slots', () => {
const vm = new Vue({
template: `
<foo ()="foo">
{{ foo }}
<template slot="one" ${syntax}="one">
{{ one }}
</template>
<template slot="two" ${syntax}="two">
{{ two }}
</template>
</foo>
`,
components: { Foo }
}).$mount()
expect(vm.$el.innerHTML.replace(/\s+/g, ' ')).toMatch(`from foo default from foo one from foo two`)
})
it('nested + named + default slots', () => {
const vm = new Vue({
template: `
<foo>
<template slot="one" ${syntax}="one">
<bar ${syntax}="bar">
{{ one }} {{ bar }}
</bar>
</template>
<template slot="two" ${syntax}="two">
<baz ${syntax}="baz">
{{ two }} {{ baz }}
</baz>
</template>
</foo>
`,
components: { Foo, Bar, Baz }
}).$mount()
expect(vm.$el.innerHTML.replace(/\s+/g, ' ')).toMatch(`from foo one from bar from foo two from baz`)
})
it('should warn slot-props usage on non-component elements', () => {
const vm = new Vue({
template: `<div ${syntax}="foo"/>`
}).$mount()
expect(`slot-props cannot be used on non-component elements`).toHaveBeenWarned()
})
}
// run tests for both full syntax and shorthand
runSuite('slot-props')
runSuite('()')
it('default + named slots', () => {
it('shorthand named slots', () => {
const vm = new Vue({
template: `
<foo ()="foo">
{{ foo }}
<template slot="one" ${syntax}="one">
<template (one)="one">
{{ one }}
</template>
<template slot="two" ${syntax}="two">
<template (two)="two">
{{ two }}
</template>
</foo>
@ -699,97 +751,47 @@ describe('Component scoped slot', () => {
expect(vm.$el.innerHTML.replace(/\s+/g, ' ')).toMatch(`from foo default from foo one from foo two`)
})
it('nested + named + default slots', () => {
it('shorthand without scope variable', () => {
const vm = new Vue({
template: `
<foo>
<template slot="one" ${syntax}="one">
<bar ${syntax}="bar">
{{ one }} {{ bar }}
</bar>
</template>
<template slot="two" ${syntax}="two">
<baz ${syntax}="baz">
{{ two }} {{ baz }}
</baz>
</template>
<template (one)>one</template>
<template (two)>two</template>
</foo>
`,
components: { Foo, Bar, Baz }
components: { Foo }
}).$mount()
expect(vm.$el.innerHTML.replace(/\s+/g, ' ')).toMatch(`from foo one from bar from foo two from baz`)
expect(vm.$el.innerHTML.replace(/\s+/g, ' ')).toMatch(`onetwo`)
})
it('should warn slot-props usage on non-component elements', () => {
it('shorthand named slots on root', () => {
const vm = new Vue({
template: `<div ${syntax}="foo"/>`
template: `
<foo (one)="one">
{{ one }}
</foo>
`,
components: { Foo }
}).$mount()
expect(`slot-props cannot be used on non-component elements`).toHaveBeenWarned()
expect(vm.$el.innerHTML.replace(/\s+/g, ' ')).toMatch(`from foo one`)
})
}
// run tests for both full syntax and shorthand
runSuite('slot-props')
runSuite('()')
it('shorthand named slots', () => {
const vm = new Vue({
template: `
<foo ()="foo">
{{ foo }}
<template (one)="one">
{{ one }}
</template>
<template (two)="two">
{{ two }}
</template>
</foo>
`,
components: { Foo }
}).$mount()
expect(vm.$el.innerHTML.replace(/\s+/g, ' ')).toMatch(`from foo default from foo one from foo two`)
})
it('shorthand without scope variable', () => {
const vm = new Vue({
template: `
<foo>
<template (one)>one</template>
<template (two)>two</template>
</foo>
`,
components: { Foo }
}).$mount()
expect(vm.$el.innerHTML.replace(/\s+/g, ' ')).toMatch(`onetwo`)
})
it('shorthand named slots on root', () => {
const vm = new Vue({
template: `
<foo (one)="one">
{{ one }}
</foo>
`,
components: { Foo }
}).$mount()
expect(vm.$el.innerHTML.replace(/\s+/g, ' ')).toMatch(`from foo one`)
})
it('dynamic shorthand', () => {
const vm = new Vue({
data: {
a: 'one',
b: 'two'
},
template: `
<foo>
<template :(a)="one">{{ one }} </template>
<template :(b)="two">{{ two }}</template>
</foo>
`,
components: { Foo }
}).$mount()
expect(vm.$el.innerHTML.replace(/\s+/g, ' ')).toMatch(`from foo one from foo two`)
it('dynamic shorthand', () => {
const vm = new Vue({
data: {
a: 'one',
b: 'two'
},
template: `
<foo>
<template :(a)="one">{{ one }} </template>
<template :(b)="two">{{ two }}</template>
</foo>
`,
components: { Foo }
}).$mount()
expect(vm.$el.innerHTML.replace(/\s+/g, ' ')).toMatch(`from foo one from foo two`)
})
})
})
}
})

4
test/unit/karma.base.config.js

@ -1,4 +1,5 @@
const alias = require('../../scripts/alias')
const featureFlags = require('../../scripts/feature-flags')
const webpack = require('webpack')
const webpackConfig = {
@ -20,7 +21,8 @@ const webpackConfig = {
__WEEX__: false,
'process.env': {
TRANSITION_DURATION: process.env.CI ? 100 : 50,
TRANSITION_BUFFER: 10
TRANSITION_BUFFER: 10,
...featureFlags
}
})
],

Loading…
Cancel
Save