--- /dev/null
+__tests__/
+__mocks__/
+dist/packages
\ No newline at end of file
--- /dev/null
+# @vue/decorators
\ No newline at end of file
--- /dev/null
+import { prop } from '../src/index'
+import { Component, createInstance } from '@vue/runtime-test'
+
+test('without options', () => {
+ let capturedThisValue
+ let capturedPropsValue
+
+ class Foo extends Component<{ p: number }> {
+ @prop
+ p: number
+
+ created() {
+ capturedThisValue = this.p
+ capturedPropsValue = this.$props.p
+ }
+ }
+
+ createInstance(Foo, {
+ p: 1
+ })
+ expect(capturedThisValue).toBe(1)
+ expect(capturedPropsValue).toBe(1)
+
+ // explicit override
+ createInstance(Foo, {
+ p: 2
+ })
+ expect(capturedThisValue).toBe(2)
+ expect(capturedPropsValue).toBe(2)
+})
+
+test('with options', () => {
+ let capturedThisValue
+ let capturedPropsValue
+
+ class Foo extends Component<{ p: number }> {
+ @prop({
+ default: 1
+ })
+ p: number
+
+ created() {
+ capturedThisValue = this.p
+ capturedPropsValue = this.$props.p
+ }
+ }
+
+ // default value
+ createInstance(Foo)
+ expect(capturedThisValue).toBe(1)
+ expect(capturedPropsValue).toBe(1)
+
+ // explicit override
+ createInstance(Foo, {
+ p: 2
+ })
+ expect(capturedThisValue).toBe(2)
+ expect(capturedPropsValue).toBe(2)
+})
--- /dev/null
+'use strict'
+
+if (process.env.NODE_ENV === 'production') {
+ module.exports = require('./dist/decorators.cjs.prod.js')
+} else {
+ module.exports = require('./dist/decorators.cjs.js')
+}
--- /dev/null
+{
+ "name": "@vue/decorators",
+ "version": "3.0.0-alpha.1",
+ "description": "@vue/decorators",
+ "main": "index.js",
+ "module": "dist/decorators.esm-bundler.js",
+ "types": "dist/index.d.ts",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/vuejs/vue.git"
+ },
+ "keywords": [
+ "vue"
+ ],
+ "author": "Evan You",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/vuejs/vue/issues"
+ },
+ "homepage": "https://github.com/vuejs/vue/tree/dev/packages/decorators#readme"
+}
\ No newline at end of file
--- /dev/null
+import { PropValidator, Component } from '@vue/runtime-core'
+
+export function prop(
+ target: Component | PropValidator<any>,
+ key?: string
+): any {
+ if (key) {
+ applyProp(target, key)
+ } else {
+ const options = target as PropValidator<any>
+ return (target: any, key: string) => {
+ applyProp(target, key, options)
+ }
+ }
+}
+
+function applyProp(target: any, key: string, options: PropValidator<any> = {}) {
+ // here `target` is the prototype of the component class
+ Object.defineProperty(target, `__prop_${key}`, {
+ value: options
+ })
+}
renderTriggered: 1
}
+// This is a special marker from the @prop decorator.
+// The decorator stores prop options on the Class' prototype as __prop_xxx
+const propPrefixRE = /^__prop_/
+
// This is called in the base component constructor and the return value is
// set on the instance as $options.
export function resolveComponentOptionsFromClass(
}
}
+ // pre-normalize array props options into object.
+ // we may need to attach more props to it (declared by decorators)
+ if (Array.isArray(options.props)) {
+ options.props = normalizePropsOptions(options.props)
+ }
+
const instanceDescriptors = Object.getOwnPropertyDescriptors(Class.prototype)
for (const key in instanceDescriptors) {
const { get, value } = instanceDescriptors[key]
// as it's already defined on the prototype
} else if (isFunction(value) && key !== 'constructor') {
if (key in reservedMethods) {
+ // lifecycle hooks / reserved methods
options[key] = value
} else {
+ // normal methods
;(options.methods || (options.methods = {}))[key] = value
}
+ } else if (propPrefixRE.test(key)) {
+ // decorator-declared props
+ const propName = key.replace(propPrefixRE, '')
+ ;(options.props || (options.props = {}))[propName] = value
}
}
+ // post-normalize all prop options into same object format
if (options.props) {
options.props = normalizePropsOptions(options.props)
}
const hasDefault = opt.hasOwnProperty('default')
const currentValue = props[key]
// default values
- if (hasDefault && currentValue === void 0) {
+ if (hasDefault && currentValue === undefined) {
const defaultValue = opt.default
props[key] = isFunction(defaultValue) ? defaultValue() : defaultValue
}
}
}
// runtime validation
- if (__DEV__) {
+ if (__DEV__ && rawData) {
validateProp(key, unwrap(rawData[key]), opt, isAbsent)
}
}
for (const key in raw) {
const opt = raw[key]
const prop = (normalized[camelize(key)] =
- isArray(opt) || isFunction(opt) ? { type: opt } : opt) as NormalizedProp
- const booleanIndex = getTypeIndex(Boolean, prop.type)
- const stringIndex = getTypeIndex(String, prop.type)
- prop[BooleanFlags.shouldCast] = booleanIndex > -1
- prop[BooleanFlags.shouldCastTrue] = booleanIndex < stringIndex
+ isArray(opt) || isFunction(opt) ? { type: opt } : opt)
+ if (prop) {
+ const booleanIndex = getTypeIndex(Boolean, prop.type)
+ const stringIndex = getTypeIndex(String, prop.type)
+ ;(prop as NormalizedProp)[BooleanFlags.shouldCast] = booleanIndex > -1
+ ;(prop as NormalizedProp)[BooleanFlags.shouldCastTrue] =
+ booleanIndex < stringIndex
+ }
}
}
return normalized
import { ComponentInstance } from './component'
import { observable } from '@vue/observer'
import { isReservedKey } from '@vue/shared'
+import { warn } from './warning'
export function initializeState(
instance: ComponentInstance,
data: any = {}
): any {
const keys = Object.keys(instance)
+ const props = instance.$options.props
for (let i = 0; i < keys.length; i++) {
const key = keys[i]
if (!isReservedKey(key)) {
- data[key] = (instance as any)[key]
+ // it's possible for a prop to be present here when it's declared with
+ // decorators and has a default value.
+ if (props && props.hasOwnProperty(key)) {
+ __DEV__ &&
+ warn(
+ `Class property "${key}" is declared as a prop but also has an initializer. ` +
+ `If you are trying to provide a default value for the prop, use the ` +
+ `prop's "default" option instead.`
+ )
+ } else {
+ data[key] = (instance as any)[key]
+ }
}
}
return data
export function renderFunctionalRoot(vnode: VNode): VNode {
const render = vnode.tag as FunctionalComponent
- const [props, attrs] = resolveProps(vnode.data, render.props)
+ const { 0: props, 1: attrs } = resolveProps(vnode.data, render.props)
let subTree
try {
subTree = render(props, vnode.slots || EMPTY_OBJ, attrs, vnode)