eslint-plugin-vuetify 2.0.4 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/configs/base.js
CHANGED
|
@@ -10,6 +10,7 @@ module.exports = {
|
|
|
10
10
|
'vuetify/no-deprecated-colors': 'error',
|
|
11
11
|
'vuetify/no-deprecated-components': 'error',
|
|
12
12
|
'vuetify/no-deprecated-events': 'error',
|
|
13
|
-
'vuetify/no-deprecated-props': 'error'
|
|
13
|
+
'vuetify/no-deprecated-props': 'error',
|
|
14
|
+
'vuetify/no-deprecated-slots': 'error'
|
|
14
15
|
}
|
|
15
16
|
};
|
|
@@ -17,7 +17,7 @@ const replacements = new Map([[/^rounded-(r|l|tr|tl|br|bl)(-.*)?$/, ([side, rest
|
|
|
17
17
|
l: 's'
|
|
18
18
|
}[side];
|
|
19
19
|
return `border-${side}${rest}`;
|
|
20
|
-
}], [/^text-xs-(left|right|center|justify)$/, ([align]) => `text-${align}`], ['scroll-y', 'overflow-y-auto'], ['hide-overflow', 'overflow-hidden'], ['show-overflow', 'overflow-visible'], ['no-wrap', 'text-no-wrap'], ['ellipsis', 'text-truncate'], ['left', 'float-left'], ['right', 'float-right'], ['display-4', 'text-h1'], ['display-3', 'text-h2'], ['display-2', 'text-h3'], ['display-1', 'text-h4'], ['headline', 'text-h5'], ['title', 'text-h6'], ['subtitle-1', 'text-subtitle-1'], ['subtitle-2', 'text-subtitle-2'], ['body-1', 'text-body-1'], ['body-2', 'text-body-2'], ['caption', 'text-caption'], ['caption', 'text-caption'], ['overline', 'text-overline'], [/^transition-(fast-out-slow-in|linear-out-slow-in|fast-out-linear-in|ease-in-out|fast-in-fast-out|swing)$/, false]]);
|
|
20
|
+
}], [/^text-xs-(left|right|center|justify)$/, ([align]) => `text-${align}`], [/hidden-(xs|sm|md|lg|xl)-only/, ([breakpoint]) => `hidden-${breakpoint}`], ['scroll-y', 'overflow-y-auto'], ['hide-overflow', 'overflow-hidden'], ['show-overflow', 'overflow-visible'], ['no-wrap', 'text-no-wrap'], ['ellipsis', 'text-truncate'], ['left', 'float-left'], ['right', 'float-right'], ['display-4', 'text-h1'], ['display-3', 'text-h2'], ['display-2', 'text-h3'], ['display-1', 'text-h4'], ['headline', 'text-h5'], ['title', 'text-h6'], ['subtitle-1', 'text-subtitle-1'], ['subtitle-2', 'text-subtitle-2'], ['body-1', 'text-body-1'], ['body-2', 'text-body-2'], ['caption', 'text-caption'], ['caption', 'text-caption'], ['overline', 'text-overline'], [/^transition-(fast-out-slow-in|linear-out-slow-in|fast-out-linear-in|ease-in-out|fast-in-fast-out|swing)$/, false]]);
|
|
21
21
|
|
|
22
22
|
// ------------------------------------------------------------------------------
|
|
23
23
|
// Rule Definition
|
|
@@ -14,6 +14,8 @@ const replacements = {
|
|
|
14
14
|
VListTileSubTitle: 'v-list-item-subtitle',
|
|
15
15
|
VJumbotron: false,
|
|
16
16
|
VToolbarSideIcon: 'v-app-bar-nav-icon',
|
|
17
|
+
VExpansionPanelHeader: 'v-expansion-panel-title',
|
|
18
|
+
VExpansionPanelContent: 'v-expansion-panel-text',
|
|
17
19
|
// Possible typos
|
|
18
20
|
VListItemSubTitle: 'v-list-item-subtitle',
|
|
19
21
|
VListTileSubtitle: 'v-list-item-subtitle',
|
|
@@ -24,17 +26,18 @@ const replacements = {
|
|
|
24
26
|
VListItemAvatar: false,
|
|
25
27
|
VListItemContent: false,
|
|
26
28
|
VListItemIcon: false,
|
|
27
|
-
VOtpInput: false,
|
|
28
29
|
VOverflowBtn: false,
|
|
29
30
|
VPicker: false,
|
|
30
31
|
VSimpleCheckbox: 'v-checkbox-btn',
|
|
31
32
|
VSparkline: false,
|
|
32
33
|
VSpeedDial: false,
|
|
33
|
-
VStepper: false,
|
|
34
34
|
VSubheader: {
|
|
35
35
|
custom: 'v-list-subheader or class="text-subheader-2"'
|
|
36
36
|
},
|
|
37
37
|
VSimpleTable: 'v-table',
|
|
38
|
+
VTabsSlider: false,
|
|
39
|
+
VTabsItems: false,
|
|
40
|
+
VTabItem: false,
|
|
38
41
|
VTimePicker: false,
|
|
39
42
|
VTreeview: false
|
|
40
43
|
};
|
|
@@ -32,6 +32,10 @@ const sizes = {
|
|
|
32
32
|
value: 'x-small'
|
|
33
33
|
}
|
|
34
34
|
};
|
|
35
|
+
const theme = {
|
|
36
|
+
dark: false,
|
|
37
|
+
light: false
|
|
38
|
+
};
|
|
35
39
|
const inputs = {
|
|
36
40
|
appendOuterIcon: 'append-icon',
|
|
37
41
|
backgroundColor: 'bg-color',
|
|
@@ -60,7 +64,6 @@ const inputs = {
|
|
|
60
64
|
name: 'variant',
|
|
61
65
|
value: 'outlined'
|
|
62
66
|
},
|
|
63
|
-
rounded: false,
|
|
64
67
|
shaped: false,
|
|
65
68
|
solo: {
|
|
66
69
|
name: 'variant',
|
|
@@ -76,14 +79,14 @@ const inputs = {
|
|
|
76
79
|
name: 'validate-on',
|
|
77
80
|
value: 'blur'
|
|
78
81
|
},
|
|
79
|
-
value: 'model-value'
|
|
82
|
+
value: 'model-value',
|
|
83
|
+
...theme
|
|
80
84
|
};
|
|
81
85
|
const select = {
|
|
82
86
|
allowOverflow: false,
|
|
83
87
|
attach: {
|
|
84
88
|
custom: ':menu-props="{ attach: true }"'
|
|
85
89
|
},
|
|
86
|
-
autoSelectFirst: false,
|
|
87
90
|
cacheItems: false,
|
|
88
91
|
deletableChips: 'closable-chips',
|
|
89
92
|
disableLookup: false,
|
|
@@ -101,10 +104,6 @@ const select = {
|
|
|
101
104
|
...inputs,
|
|
102
105
|
dense: false
|
|
103
106
|
};
|
|
104
|
-
const theme = {
|
|
105
|
-
dark: false,
|
|
106
|
-
light: false
|
|
107
|
-
};
|
|
108
107
|
const link = {
|
|
109
108
|
append: false,
|
|
110
109
|
exactActiveClass: false,
|
|
@@ -196,7 +195,23 @@ const replacements = {
|
|
|
196
195
|
avatar: false,
|
|
197
196
|
mode: false,
|
|
198
197
|
origin: false,
|
|
199
|
-
overlap: false
|
|
198
|
+
overlap: false,
|
|
199
|
+
bottom: {
|
|
200
|
+
name: 'location',
|
|
201
|
+
value: 'bottom'
|
|
202
|
+
},
|
|
203
|
+
left: {
|
|
204
|
+
name: 'location',
|
|
205
|
+
value: 'left'
|
|
206
|
+
},
|
|
207
|
+
right: {
|
|
208
|
+
name: 'location',
|
|
209
|
+
value: 'right'
|
|
210
|
+
},
|
|
211
|
+
top: {
|
|
212
|
+
name: 'location',
|
|
213
|
+
value: 'top'
|
|
214
|
+
}
|
|
200
215
|
},
|
|
201
216
|
VBanner: {
|
|
202
217
|
app: false,
|
|
@@ -377,7 +392,9 @@ const replacements = {
|
|
|
377
392
|
},
|
|
378
393
|
selected: 'value',
|
|
379
394
|
textColor: false,
|
|
380
|
-
...link
|
|
395
|
+
...link,
|
|
396
|
+
...sizes,
|
|
397
|
+
...theme
|
|
381
398
|
},
|
|
382
399
|
VChipGroup: {
|
|
383
400
|
activeClass: 'selected-class',
|
|
@@ -393,6 +410,39 @@ const replacements = {
|
|
|
393
410
|
hideModeSwitch: false,
|
|
394
411
|
value: 'model-value'
|
|
395
412
|
},
|
|
413
|
+
VDataTable: {
|
|
414
|
+
serverItemsLength: {
|
|
415
|
+
custom: '<v-data-table-server>'
|
|
416
|
+
},
|
|
417
|
+
itemClass: {
|
|
418
|
+
custom: 'row-props'
|
|
419
|
+
},
|
|
420
|
+
itemStyle: {
|
|
421
|
+
custom: 'row-props'
|
|
422
|
+
},
|
|
423
|
+
sortDesc: {
|
|
424
|
+
custom: 'sort-by'
|
|
425
|
+
},
|
|
426
|
+
groupDesc: {
|
|
427
|
+
custom: 'group-by'
|
|
428
|
+
}
|
|
429
|
+
},
|
|
430
|
+
VDatePicker: {
|
|
431
|
+
activePicker: 'view-mode',
|
|
432
|
+
pickerDate: {
|
|
433
|
+
custom: 'separate month and year props'
|
|
434
|
+
},
|
|
435
|
+
locale: false,
|
|
436
|
+
localeFirstDayOfYear: false,
|
|
437
|
+
firstDayOfWeek: false,
|
|
438
|
+
dayFormat: false,
|
|
439
|
+
weekdayFormat: false,
|
|
440
|
+
monthFormat: false,
|
|
441
|
+
yearFormat: false,
|
|
442
|
+
headerDateFormat: false,
|
|
443
|
+
titleDateFormat: false,
|
|
444
|
+
range: false
|
|
445
|
+
},
|
|
396
446
|
VExpansionPanels: {
|
|
397
447
|
accordion: {
|
|
398
448
|
name: 'variant',
|
|
@@ -467,6 +517,7 @@ const replacements = {
|
|
|
467
517
|
nudgeTop: {
|
|
468
518
|
custom: 'offset'
|
|
469
519
|
},
|
|
520
|
+
nudgeWidth: false,
|
|
470
521
|
offsetOverflow: false,
|
|
471
522
|
offsetX: false,
|
|
472
523
|
offsetY: false,
|
|
@@ -641,6 +692,7 @@ const replacements = {
|
|
|
641
692
|
VRadioGroup: {
|
|
642
693
|
activeClass: false,
|
|
643
694
|
backgroundColor: false,
|
|
695
|
+
row: 'inline',
|
|
644
696
|
column: false,
|
|
645
697
|
multiple: false,
|
|
646
698
|
...inputs
|
|
@@ -658,8 +710,7 @@ const replacements = {
|
|
|
658
710
|
height: false,
|
|
659
711
|
loading: false,
|
|
660
712
|
inverseLabel: false,
|
|
661
|
-
...inputs
|
|
662
|
-
...theme
|
|
713
|
+
...inputs
|
|
663
714
|
},
|
|
664
715
|
VRangeSlider: {
|
|
665
716
|
backgroundColor: false,
|
|
@@ -674,8 +725,7 @@ const replacements = {
|
|
|
674
725
|
height: false,
|
|
675
726
|
loading: false,
|
|
676
727
|
inverseLabel: false,
|
|
677
|
-
...inputs
|
|
678
|
-
...theme
|
|
728
|
+
...inputs
|
|
679
729
|
},
|
|
680
730
|
VRating: {
|
|
681
731
|
backgroundColor: false,
|
|
@@ -852,6 +902,7 @@ const replacements = {
|
|
|
852
902
|
nudgeTop: {
|
|
853
903
|
custom: 'offset'
|
|
854
904
|
},
|
|
905
|
+
nudgeWidth: false,
|
|
855
906
|
positionX: false,
|
|
856
907
|
positionY: false,
|
|
857
908
|
right: {
|
|
@@ -895,11 +946,35 @@ module.exports = {
|
|
|
895
946
|
schema: [],
|
|
896
947
|
messages: {
|
|
897
948
|
replacedWith: `'{{ a }}' has been replaced with '{{ b }}'`,
|
|
898
|
-
removed: `'{{ name }}' has been removed
|
|
949
|
+
removed: `'{{ name }}' has been removed`,
|
|
950
|
+
combined: `multiple {{ a }} attributes have been combined`
|
|
899
951
|
}
|
|
900
952
|
},
|
|
901
953
|
create(context) {
|
|
902
954
|
return context.parserServices.defineTemplateBodyVisitor({
|
|
955
|
+
VStartTag(tag) {
|
|
956
|
+
const attrGroups = {};
|
|
957
|
+
tag.attributes.forEach(attr => {
|
|
958
|
+
if (['location'].includes(attr.key.name)) {
|
|
959
|
+
attrGroups[attr.key.name] = attrGroups[attr.key.name] ?? [];
|
|
960
|
+
attrGroups[attr.key.name].push(attr);
|
|
961
|
+
}
|
|
962
|
+
});
|
|
963
|
+
Object.values(attrGroups).forEach(attrGroup => {
|
|
964
|
+
const [head, ...tail] = attrGroup;
|
|
965
|
+
if (!tail.length) return;
|
|
966
|
+
context.report({
|
|
967
|
+
messageId: 'combined',
|
|
968
|
+
data: {
|
|
969
|
+
a: head.key.name
|
|
970
|
+
},
|
|
971
|
+
node: head,
|
|
972
|
+
fix(fixer) {
|
|
973
|
+
return [fixer.replaceText(head.value, `"${attrGroup.map(a => a.value.value).join(' ')}"`), ...tail.map(a => fixer.remove(a))];
|
|
974
|
+
}
|
|
975
|
+
});
|
|
976
|
+
});
|
|
977
|
+
},
|
|
903
978
|
VAttribute(attr) {
|
|
904
979
|
if (attr.directive && (attr.key.name.name !== 'bind' || !attr.key.argument)) return;
|
|
905
980
|
const tag = classify(attr.parent.parent.rawName);
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
classify,
|
|
5
|
+
getAttributes
|
|
6
|
+
} = require('../util/helpers');
|
|
7
|
+
const groups = [{
|
|
8
|
+
components: ['VDialog', 'VMenu', 'VTooltip'],
|
|
9
|
+
slots: ['activator'],
|
|
10
|
+
handler(context, node, directive, param) {
|
|
11
|
+
if (param.type === 'Identifier') {
|
|
12
|
+
// #activator="data"
|
|
13
|
+
const boundVariables = {};
|
|
14
|
+
node.variables.find(variable => variable.id.name === param.name)?.references.forEach(ref => {
|
|
15
|
+
if (ref.id.parent.type !== 'MemberExpression') return;
|
|
16
|
+
if (
|
|
17
|
+
// v-bind="data.props"
|
|
18
|
+
ref.id.parent.property.name === 'props' && ref.id.parent.parent.parent.directive && ref.id.parent.parent.parent.key.name.name === 'bind' && !ref.id.parent.parent.parent.key.argument) return;
|
|
19
|
+
if (ref.id.parent.property.name === 'on') {
|
|
20
|
+
boundVariables.on = ref.id;
|
|
21
|
+
} else if (ref.id.parent.property.name === 'attrs') {
|
|
22
|
+
boundVariables.attrs = ref.id;
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
if (boundVariables.on) {
|
|
26
|
+
const ref = boundVariables.on;
|
|
27
|
+
context.report({
|
|
28
|
+
node: ref,
|
|
29
|
+
messageId: 'changedProps',
|
|
30
|
+
data: {
|
|
31
|
+
component: node.parent.name,
|
|
32
|
+
slot: directive.key.argument.name
|
|
33
|
+
},
|
|
34
|
+
fix(fixer) {
|
|
35
|
+
return fixer.replaceText(ref.parent.parent.parent, `v-bind="${param.name}.props"`);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
if (boundVariables.attrs) {
|
|
40
|
+
const ref = boundVariables.attrs;
|
|
41
|
+
if (!boundVariables.on) {
|
|
42
|
+
context.report({
|
|
43
|
+
node: boundVariables.attrs,
|
|
44
|
+
messageId: 'invalidProps'
|
|
45
|
+
});
|
|
46
|
+
} else {
|
|
47
|
+
context.report({
|
|
48
|
+
node: ref,
|
|
49
|
+
messageId: 'changedProps',
|
|
50
|
+
data: {
|
|
51
|
+
component: node.parent.name,
|
|
52
|
+
slot: directive.key.argument.name
|
|
53
|
+
},
|
|
54
|
+
fix(fixer) {
|
|
55
|
+
return fixer.removeRange([ref.parent.parent.parent.range[0] - 1, ref.parent.parent.parent.range[1]]);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
} else if (param.type === 'ObjectPattern') {
|
|
61
|
+
// #activator="{ on, attrs }"
|
|
62
|
+
const boundVariables = {};
|
|
63
|
+
param.properties.forEach(prop => {
|
|
64
|
+
node.variables.find(variable => variable.id.name === prop.value.name)?.references.forEach(ref => {
|
|
65
|
+
if (prop.key.name === 'on') {
|
|
66
|
+
boundVariables.on = {
|
|
67
|
+
prop,
|
|
68
|
+
id: ref.id
|
|
69
|
+
};
|
|
70
|
+
} else if (prop.key.name === 'attrs') {
|
|
71
|
+
boundVariables.attrs = {
|
|
72
|
+
prop,
|
|
73
|
+
id: ref.id
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
if (boundVariables.on || boundVariables.attrs) {
|
|
79
|
+
if (boundVariables.attrs && !boundVariables.on) {
|
|
80
|
+
context.report({
|
|
81
|
+
node: boundVariables.attrs.prop.key,
|
|
82
|
+
messageId: 'invalidProps'
|
|
83
|
+
});
|
|
84
|
+
} else {
|
|
85
|
+
context.report({
|
|
86
|
+
node: param,
|
|
87
|
+
messageId: 'changedProps',
|
|
88
|
+
data: {
|
|
89
|
+
component: node.parent.name,
|
|
90
|
+
slot: directive.key.argument.name
|
|
91
|
+
},
|
|
92
|
+
*fix(fixer) {
|
|
93
|
+
if (boundVariables.on) {
|
|
94
|
+
const ref = boundVariables.on;
|
|
95
|
+
yield fixer.replaceText(ref.prop, 'props');
|
|
96
|
+
yield fixer.replaceText(ref.id.parent.parent, `v-bind="props"`);
|
|
97
|
+
}
|
|
98
|
+
if (boundVariables.attrs) {
|
|
99
|
+
const template = context.parserServices.getTemplateBodyTokenStore();
|
|
100
|
+
const ref = boundVariables.attrs;
|
|
101
|
+
const isLast = ref.prop === param.properties.at(-1);
|
|
102
|
+
if (isLast) {
|
|
103
|
+
const comma = template.getTokenBefore(ref.prop, {
|
|
104
|
+
filter: token => token.value === ','
|
|
105
|
+
});
|
|
106
|
+
if (comma) {
|
|
107
|
+
yield fixer.removeRange([comma.start, ref.prop.end]);
|
|
108
|
+
} else {
|
|
109
|
+
yield fixer.removeRange([ref.prop.start - 1, ref.prop.end]);
|
|
110
|
+
}
|
|
111
|
+
} else {
|
|
112
|
+
const comma = template.getTokenAfter(ref.prop, {
|
|
113
|
+
filter: token => token.value === ','
|
|
114
|
+
});
|
|
115
|
+
if (comma) {
|
|
116
|
+
yield fixer.removeRange([ref.prop.start - 1, comma.end]);
|
|
117
|
+
} else {
|
|
118
|
+
yield fixer.removeRange([ref.prop.start - 1, ref.prop.end]);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
yield fixer.removeRange([ref.id.parent.parent.range[0] - 1, ref.id.parent.parent.range[1]]);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
} else {
|
|
128
|
+
context.report({
|
|
129
|
+
node: directive,
|
|
130
|
+
messageId: 'invalidProps'
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}, {
|
|
135
|
+
components: ['VSelect', 'VAutocomplete', 'VCombobox'],
|
|
136
|
+
slots: ['selection'],
|
|
137
|
+
handler(context, node, directive, param) {
|
|
138
|
+
if (!getAttributes(node.parent).some(attr => ['chips', 'closable-chips'].includes(attr.name))) return;
|
|
139
|
+
context.report({
|
|
140
|
+
node: directive,
|
|
141
|
+
messageId: 'renamed',
|
|
142
|
+
data: {
|
|
143
|
+
component: node.parent.name,
|
|
144
|
+
slot: directive.key.argument.name,
|
|
145
|
+
newSlot: 'chip'
|
|
146
|
+
},
|
|
147
|
+
fix(fixer) {
|
|
148
|
+
return fixer.replaceText(directive.key.argument, 'chip');
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
}];
|
|
153
|
+
|
|
154
|
+
// ------------------------------------------------------------------------------
|
|
155
|
+
// Rule Definition
|
|
156
|
+
// ------------------------------------------------------------------------------
|
|
157
|
+
|
|
158
|
+
module.exports = {
|
|
159
|
+
meta: {
|
|
160
|
+
docs: {
|
|
161
|
+
description: 'Prevent the use of removed and deprecated slots.',
|
|
162
|
+
category: 'recommended'
|
|
163
|
+
},
|
|
164
|
+
fixable: 'code',
|
|
165
|
+
schema: [],
|
|
166
|
+
messages: {
|
|
167
|
+
renamed: `{{ component }}'s '{{ slot }}' slot has been renamed to '{{ newSlot }}'`,
|
|
168
|
+
changedProps: `{{ component }}'s '{{ slot }}' slot has changed props`,
|
|
169
|
+
invalidProps: `Slot has invalid props`
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
create(context) {
|
|
173
|
+
let scopeStack;
|
|
174
|
+
return context.parserServices.defineTemplateBodyVisitor({
|
|
175
|
+
VElement(node) {
|
|
176
|
+
scopeStack = {
|
|
177
|
+
parent: scopeStack,
|
|
178
|
+
nodes: scopeStack ? [...scopeStack.nodes] : []
|
|
179
|
+
};
|
|
180
|
+
for (const variable of node.variables) {
|
|
181
|
+
scopeStack.nodes.push(variable.id);
|
|
182
|
+
}
|
|
183
|
+
if (node.name !== 'template' || node.parent.type !== 'VElement') return;
|
|
184
|
+
for (const group of groups) {
|
|
185
|
+
if (!group.components.includes(classify(node.parent.name)) && !group.components.includes(node.parent.name)) continue;
|
|
186
|
+
const directive = node.startTag.attributes.find(attr => {
|
|
187
|
+
return attr.directive && attr.key.name.name === 'slot' && group.slots.includes(attr.key.argument?.name);
|
|
188
|
+
});
|
|
189
|
+
if (!directive || !directive.value || directive.value.type !== 'VExpressionContainer' || !directive.value.expression || directive.value.expression.params.length !== 1) continue;
|
|
190
|
+
const param = directive.value.expression.params[0];
|
|
191
|
+
group.handler(context, node, directive, param);
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
'VElement:exit'() {
|
|
195
|
+
scopeStack = scopeStack && scopeStack.parent;
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
};
|