1 |
|
2 | import courier from './courier';
|
3 | import normalizeSlots from './normalizeSlots';
|
4 |
|
5 | import type {
|
6 | Ctor,
|
7 | CreateRenderFnOptions,
|
8 | CreateRenderFn,
|
9 | CreateRenderFnc,
|
10 | } from './annotations';
|
11 |
|
12 | const isObject = test => Object.prototype.toString.call(test) === '[object Object]';
|
13 | const isFn = test => Object.prototype.toString.call(test) === '[object Function]';
|
14 |
|
15 | const justBindOptions = [
|
16 | 'listeners',
|
17 | 'nativeOn',
|
18 | 'scopedSlots',
|
19 | ];
|
20 |
|
21 | const justBindFn = key => justBindOptions.indexOf(key) > -1;
|
22 |
|
23 | const getOptionsKeys = options => Object
|
24 | .keys(options)
|
25 | .concat(['listeners', 'props', 'attrs'])
|
26 | .filter((k, i, a) => a.indexOf(k) === i);
|
27 |
|
28 | const createOptionHandlers = (originalOptions, keys) => {
|
29 | const options: {
|
30 | [dataName: string]: Function
|
31 | } = {};
|
32 |
|
33 | keys.forEach(key => {
|
34 | const option = originalOptions[key];
|
35 |
|
36 | if (!option){
|
37 | options[key] = owner => owner;
|
38 | return;
|
39 | }
|
40 |
|
41 | if (isFn(option)){
|
42 |
|
43 | options[key] = option;
|
44 | return;
|
45 | }
|
46 |
|
47 | if (isObject(option)){
|
48 | const optionKeys = Object.keys(option);
|
49 |
|
50 | if (!optionKeys.some(k => isFn(option[k]))){
|
51 | options[key] = (owner) => Object.assign({}, owner, option);
|
52 | return;
|
53 | }
|
54 |
|
55 | options[key] = function(owner) {
|
56 | const result = Object.assign({}, owner);
|
57 | const justBind = justBindFn(key);
|
58 |
|
59 | optionKeys.forEach(k => {
|
60 | let value = option && option[k];
|
61 |
|
62 | if (isFn(value)){
|
63 | if (justBind){
|
64 | value = value.bind(this);
|
65 | }else{
|
66 | value = value.call(this, owner);
|
67 | }
|
68 | }
|
69 | result[k] = value;
|
70 | });
|
71 | return result;
|
72 | };
|
73 | return;
|
74 | }
|
75 |
|
76 | options[key] = () => option;
|
77 | });
|
78 |
|
79 | return options;
|
80 | };
|
81 |
|
82 | const preprocessOptions = (originalOptions) => {
|
83 | const keys = getOptionsKeys(originalOptions);
|
84 | const options = createOptionHandlers(originalOptions, keys);
|
85 |
|
86 | return (context, isFunctional) => {
|
87 | const result: {
|
88 | on: Object,
|
89 | props: Object,
|
90 | attrs: Object,
|
91 | scopedSlots?: Object,
|
92 | } = {
|
93 | on: {},
|
94 | props: {},
|
95 | attrs: {},
|
96 | };
|
97 |
|
98 | keys.forEach(key => {
|
99 | const owner = isFunctional ?
|
100 | context[key] || context.data[key] :
|
101 | context[`$${key}`];
|
102 |
|
103 | const value = options[key].call(context, owner);
|
104 |
|
105 | if (key === 'listeners'){
|
106 | key = 'on';
|
107 | }
|
108 |
|
109 | result[key] = value;
|
110 | });
|
111 |
|
112 | return result;
|
113 | };
|
114 | };
|
115 |
|
116 |
|
117 | export const createRenderFn: CreateRenderFn = (Component, options) => {
|
118 | const getData = preprocessOptions(options || {});
|
119 |
|
120 | return function renderHoc(
|
121 | h: (
|
122 | ctor: Object,
|
123 | data: Object,
|
124 | slots: Array<any>
|
125 | ) => any,
|
126 | context?: Object
|
127 | ) {
|
128 |
|
129 | const data = getData(context || this, !!context);
|
130 | const scopedSlots: Object = (context && context.data && context.data.scopedSlots) ||
|
131 | (this && this.$scopedSlots);
|
132 | const slots: Array<any> = (context && context.children) || (this && this.$slots && normalizeSlots(this.$slots)) || [];
|
133 |
|
134 | data.scopedSlots = data.scopedSlots || scopedSlots;
|
135 |
|
136 | return h(Component, data, slots);
|
137 | };
|
138 | };
|
139 |
|
140 | export const createRenderFnc: CreateRenderFnc = courier(2, (
|
141 | options: CreateRenderFnOptions,
|
142 | Component: Ctor
|
143 | ) => {
|
144 | return createRenderFn(Component, options);
|
145 | });
|