Skip to content

Commit 7bb7dfe

Browse files
committed
merge stage2/3 and pass all tests
2 parents 8413472 + acfdaae commit 7bb7dfe

40 files changed

+342
-183
lines changed

docs/delare.d.ts

Lines changed: 0 additions & 3 deletions
This file was deleted.

docs/en/_sidebar.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,6 @@
2323
- Custom Decorator
2424
- [Custom Decorator](/en/custom/custom.md)
2525
- Compatibility
26-
- [reflect-metadata](/en/compatibility/reflect-metadata/reflect-metadata.md)
26+
- [reflect-metadata](/en/compatibility/reflect-metadata/reflect-metadata.md)
27+
- Next version
28+
- [Next version](/en/next-version/next-version.md)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
2+
import { Component, Vue, toNative, Prop } from 'vue-facing-decorator'
3+
4+
@Component({
5+
name: "MyComponent"
6+
})
7+
class MyComponent extends Vue {
8+
@Prop
9+
prop!: string
10+
11+
field = this.prop // this is deprecated, it will be undefined
12+
}
13+
14+
15+
export default toNative(MyComponent)
16+
17+
export { MyComponent }
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
2+
import { Component, Vue, toNative } from 'vue-facing-decorator'
3+
4+
@Component({
5+
name: "MyComponent"
6+
})
7+
class MyComponent extends Vue {
8+
9+
}
10+
11+
/*
12+
Cast to vue options API by toNative
13+
{
14+
name:"MyComponent"
15+
}
16+
*/
17+
18+
export default toNative(MyComponent)
19+
20+
export { MyComponent }
21+
22+
/*
23+
import MyComponent from 'MyComponent.vue'
24+
25+
@Component({
26+
components:[MyComponent] // use it as a component of another component
27+
})
28+
29+
30+
//or
31+
32+
33+
import { createApp } from 'vue'
34+
import MyComponent from 'MyComponent.vue'
35+
36+
createApp(MyComponent).mount('#root') // or other places which vue want an options API component
37+
*/
38+
39+
/*
40+
import { Component, Vue, toNative } from 'vue-facing-decorator'
41+
import { MyComponent } from 'MyComponent.vue'
42+
43+
// Extends component constructor directly
44+
45+
@Component
46+
class AnotherComponent extends MyComponent {
47+
48+
}
49+
50+
*/

docs/en/next-version/next-version.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
## Next version
2+
3+
We published a preview next version that is compatible with JavaScript decorator stage 3. To migrate to preview next vewsion, you need to change your project with some break changes.
4+
5+
In 3.x, decorator `Component` is same to `ComponentBase`, and you should cast class component to vue options API manually, see Breaking changes secion.
6+
7+
## Install
8+
9+
* `npm install vue-facing-decorator@beta`
10+
11+
* Update typescript to 5.x
12+
13+
* Set `compilerOptions.experimentalDecorators` to `false`, this will enable stage 3 decorator.
14+
15+
## Breaking changes
16+
17+
### Cast class component to vue options API
18+
19+
You must use `toNative` to cast a class component to vue options API, after that, the casted component could be used as a native vue component in where vue accepts it.
20+
21+
[](./breaking-changes-toNative.ts ':include :type=code typescript')
22+
23+
### Depreactate init class property despends on another in constructor
24+
25+
This is not allowed now.
26+
27+
[](./breaking-changes-classProperty.ts ':include :type=code typescript')
28+
29+
### Remove `index-return-cons`
30+
31+
Remove `vue-facing-decorator/dist/index-return-cons`, you won't need this if `toNative` exists.

package.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "vue-facing-decorator",
3-
"version": "2.1.20",
3+
"version": "3.0.0",
44
"description": "Vue typescript class and decorator based component.",
55
"main": "dist/index.js",
66
"module": "dist/esm/index.js",
@@ -17,7 +17,9 @@
1717
],
1818
"scripts": {
1919
"test-build": "npm run build && npm run test",
20-
"test": "mocha -r ts-node/register test/test.ts",
20+
"test":"npm run test-stage2 && npm run test-stage3",
21+
"test-stage2": "env TS_NODE_COMPILER_OPTIONS='{\"experimentalDecorators\": true }' mocha -r ts-node/register test/test.ts",
22+
"test-stage3": "env TS_NODE_COMPILER_OPTIONS='{\"experimentalDecorators\": false }' mocha -r ts-node/register test/test.ts",
2123
"build": "npm run build:cjs && npm run build:esm",
2224
"build:cjs": "./node_modules/.bin/tsc",
2325
"build:esm": "./node_modules/.bin/tsc -outDir dist/esm -module ES6",
@@ -27,7 +29,6 @@
2729
},
2830
"author": "",
2931
"license": "MIT",
30-
"types": "dist/index.d.ts",
3132
"peerDependencies": {
3233
"vue": "^3.0.0"
3334
},
@@ -52,5 +53,4 @@
5253
"type": "git",
5354
"url": "[email protected]:facing-dev/vue-facing-decorator.git"
5455
}
55-
56-
}
56+
}

src/component.ts

Lines changed: 51 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@ import { CustomRecords } from './custom/custom'
1515
import type { SetupContext } from 'vue';
1616
import type { OptionBuilder } from './optionBuilder'
1717
import type { VueCons } from './index'
18+
import * as DecoratorCompatible from './deco3/utils'
1819
export type Cons = VueCons
1920

2021
type SetupFunction<T> = (this: void, props: Readonly<any>, ctx: SetupContext<any>) => T | Promise<T>
2122
export type OptionSetupFunction = SetupFunction<any>
2223
export type ComponentSetupFunction = SetupFunction<Record<string, any>>
2324
function ComponentOption(cons: Cons, extend?: any) {
24-
2525
const optionBuilder: OptionBuilder = {}
2626
optionSetup(cons, optionBuilder)
2727
optionVModel(cons, optionBuilder)
@@ -38,6 +38,7 @@ function ComponentOption(cons: Cons, extend?: any) {
3838
const setupFunction: OptionSetupFunction | undefined = optionBuilder.setup ? function (props, ctx) {
3939
return optionBuilder.setup!(props, ctx)
4040
} : undefined
41+
4142
const raw = {
4243
setup: setupFunction,
4344
data() {
@@ -89,33 +90,40 @@ function buildComponent(cons: Cons, arg: ComponentOption, extend?: any): any {
8990
option.emits = emits
9091

9192

93+
9294
CustomRecords.forEach(rec => {
9395
rec.creator.apply({}, [option, rec.key])
9496
})
9597

96-
if (arg.setup) {
97-
if (!option.setup) {
98-
option.setup = arg.setup
99-
} else {
100-
const oldSetup: OptionSetupFunction = option.setup
101-
const newSetup: ComponentSetupFunction = arg.setup
102-
103-
const setup: ComponentSetupFunction = function (props, ctx) {
104-
const newRet = newSetup(props, ctx)
105-
const oldRet = oldSetup(props, ctx)
106-
if (oldRet instanceof Promise || newRet instanceof Promise) {
107-
return Promise.all([newRet, oldRet]).then((arr) => {
108-
return Object.assign({}, arr[0], arr[1])
109-
})
110-
} else {
111-
112-
return Object.assign({}, newRet, oldRet)
113-
}
11498

99+
arg.setup ??= function () { return {} }
100+
101+
if (!option.setup) {
102+
103+
option.setup = arg.setup
104+
} else {
105+
106+
const oldSetup: OptionSetupFunction = option.setup
107+
const newSetup: ComponentSetupFunction = arg.setup
108+
109+
const setup: ComponentSetupFunction = function (props, ctx) {
110+
const newRet = newSetup(props, ctx)
111+
const oldRet = oldSetup(props, ctx)
112+
if (oldRet instanceof Promise || newRet instanceof Promise) {
113+
return Promise.all([newRet, oldRet]).then((arr) => {
114+
const ret = Object.assign({}, arr[0], arr[1])
115+
return ret
116+
})
117+
} else {
118+
119+
const ret = Object.assign({}, newRet, oldRet)
120+
return ret
115121
}
116-
option.setup = setup
122+
117123
}
124+
option.setup = setup
118125
}
126+
119127
if (arg.options) {
120128
Object.assign(option, arg.options)
121129
}
@@ -138,27 +146,36 @@ function build(cons: Cons, option: ComponentOption) {
138146
}
139147
const component = buildComponent(cons, option, superSlot === null ? undefined : superSlot.cachedVueComponent)
140148
component.__vfdConstructor = cons
141-
slot.cachedVueComponent = component
142-
149+
slot.cachedVueComponent = component;
150+
(cons as any).__vccOpts = component
143151
}
144-
function _Component(arg: ComponentConsOption, cb: (cons: Cons, option: ComponentOption) => any) {
152+
function _Component(cb: (cons: Cons, option: ComponentOption) => any, arg: ComponentConsOption, ctx?: ClassDecoratorContext) {
145153
if (typeof arg === 'function') {
146-
return cb(arg, {})
154+
return DecoratorCompatible.compatibleClassDecorator(function (cons: Cons) {
155+
return cb(cons, {})
156+
})(arg, ctx)
147157
}
148-
return function (cons: Cons) {
158+
return DecoratorCompatible.compatibleClassDecorator(function (cons: Cons) {
149159
return cb(cons, arg)
150-
}
160+
})
151161
}
152-
export function ComponentBase(arg: ComponentConsOption): any {
153-
return _Component(arg, function (cons: Cons, option: ComponentOption) {
162+
export function ComponentBase(arg: ComponentConsOption, ctx?: ClassDecoratorContext): any {
163+
return _Component(function (cons: Cons, option: ComponentOption) {
154164
build(cons, option)
155165
return cons
156-
})
166+
}, arg, ctx)
157167
}
158168

159-
export function Component(arg: ComponentConsOption): any {
160-
return _Component(arg, function (cons: Cons, option: ComponentOption) {
161-
build(cons, option)
162-
return obtainSlot(cons.prototype).cachedVueComponent
163-
})
169+
export const Component = ComponentBase
170+
171+
export function toNative<T extends Cons>(cons: T): T {
172+
const slot = obtainSlot(cons.prototype)
173+
if (!slot.inComponent) {
174+
throw 'to native 1'
175+
}
176+
const cached = slot.cachedVueComponent
177+
if (!cached) {
178+
throw 'to native 2'
179+
}
180+
return cached
164181
}

src/custom/custom.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import {compatibleMemberDecorator} from '../deco3/utils'
12
type Creator = { (options: any, key: string): void }
23
interface Record {
34
key: string
@@ -8,12 +9,12 @@ interface Record {
89
export const CustomRecords: Record[] = []
910

1011
export function createDecorator(creator: Creator) {
11-
return function (proto: any, key: string) {
12+
return compatibleMemberDecorator(function (proto: any, key: string) {
1213
CustomRecords.push({
1314
key,
1415
creator
1516
})
16-
}
17+
})
1718
}
1819

1920

src/deco3/utils.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { obtainSlot } from '../utils'
2+
export const Compatible: {
3+
fakePrototype?: any,
4+
5+
} = {
6+
7+
}
8+
export function compatibleClassDecorator(deco: Function) {
9+
return function (arg: any, ctx?: DecoratorContext) {
10+
11+
if (ctx) {//stage 3
12+
13+
if (ctx.kind !== 'class') {
14+
throw 'deco stage 3 class'
15+
}
16+
const proto = Compatible.fakePrototype ??= {}
17+
const slot = obtainSlot(proto)
18+
delete Compatible.fakePrototype
19+
20+
obtainSlot(arg.prototype, slot)
21+
const ret = deco(arg)
22+
23+
return ret
24+
}
25+
else {
26+
27+
return deco(arg)
28+
}
29+
}
30+
}
31+
32+
export function compatibleMemberDecorator(deco: Function) {
33+
return function (arg: any, ctx: DecoratorContext | string) {
34+
if (typeof ctx === 'object') {//stage 3
35+
const proto = Compatible.fakePrototype ??= {};
36+
proto[ctx.name!] = arg
37+
return deco(proto, ctx.name)
38+
} else {
39+
return deco(arg, ctx)
40+
}
41+
}
42+
}

src/index-return-cons.ts

Lines changed: 0 additions & 19 deletions
This file was deleted.

0 commit comments

Comments
 (0)