@vue/compiler-core 3.1.1 → 3.1.2
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/dist/compiler-core.cjs.js +129 -55
- package/dist/compiler-core.cjs.prod.js +129 -55
- package/dist/compiler-core.d.ts +6 -0
- package/dist/compiler-core.esm-bundler.js +129 -55
- package/package.json +2 -2
|
@@ -356,18 +356,59 @@ function isCoreComponent(tag) {
|
|
|
356
356
|
}
|
|
357
357
|
const nonIdentifierRE = /^\d|[^\$\w]/;
|
|
358
358
|
const isSimpleIdentifier = (name) => !nonIdentifierRE.test(name);
|
|
359
|
-
const
|
|
359
|
+
const validFirstIdentCharRE = /[A-Za-z_$\xA0-\uFFFF]/;
|
|
360
|
+
const validIdentCharRE = /[\.\w$\xA0-\uFFFF]/;
|
|
361
|
+
const whitespaceRE = /\s+[.[]\s*|\s*[.[]\s+/g;
|
|
362
|
+
/**
|
|
363
|
+
* Simple lexer to check if an expression is a member expression. This is
|
|
364
|
+
* lax and only checks validity at the root level (i.e. does not validate exps
|
|
365
|
+
* inside square brackets), but it's ok since these are only used on template
|
|
366
|
+
* expressions and false positives are invalid expressions in the first place.
|
|
367
|
+
*/
|
|
360
368
|
const isMemberExpression = (path) => {
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
369
|
+
// remove whitespaces around . or [ first
|
|
370
|
+
path = path.trim().replace(whitespaceRE, s => s.trim());
|
|
371
|
+
let state = 0 /* inMemberExp */;
|
|
372
|
+
let prevState = 0 /* inMemberExp */;
|
|
373
|
+
let currentOpenBracketCount = 0;
|
|
374
|
+
let currentStringType = null;
|
|
375
|
+
for (let i = 0; i < path.length; i++) {
|
|
376
|
+
const char = path.charAt(i);
|
|
377
|
+
switch (state) {
|
|
378
|
+
case 0 /* inMemberExp */:
|
|
379
|
+
if (char === '[') {
|
|
380
|
+
prevState = state;
|
|
381
|
+
state = 1 /* inBrackets */;
|
|
382
|
+
currentOpenBracketCount++;
|
|
383
|
+
}
|
|
384
|
+
else if (!(i === 0 ? validFirstIdentCharRE : validIdentCharRE).test(char)) {
|
|
385
|
+
return false;
|
|
386
|
+
}
|
|
387
|
+
break;
|
|
388
|
+
case 1 /* inBrackets */:
|
|
389
|
+
if (char === `'` || char === `"` || char === '`') {
|
|
390
|
+
prevState = state;
|
|
391
|
+
state = 2 /* inString */;
|
|
392
|
+
currentStringType = char;
|
|
393
|
+
}
|
|
394
|
+
else if (char === `[`) {
|
|
395
|
+
currentOpenBracketCount++;
|
|
396
|
+
}
|
|
397
|
+
else if (char === `]`) {
|
|
398
|
+
if (!--currentOpenBracketCount) {
|
|
399
|
+
state = prevState;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
break;
|
|
403
|
+
case 2 /* inString */:
|
|
404
|
+
if (char === currentStringType) {
|
|
405
|
+
state = prevState;
|
|
406
|
+
currentStringType = null;
|
|
407
|
+
}
|
|
408
|
+
break;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
return !currentOpenBracketCount;
|
|
371
412
|
};
|
|
372
413
|
function getInnerRange(loc, offset, length) {
|
|
373
414
|
const source = loc.source.substr(offset, length);
|
|
@@ -1043,41 +1084,17 @@ function parseTag(context, type, parent) {
|
|
|
1043
1084
|
}
|
|
1044
1085
|
}
|
|
1045
1086
|
let tagType = 0 /* ELEMENT */;
|
|
1046
|
-
|
|
1047
|
-
if (!context.inVPre && !options.isCustomElement(tag)) {
|
|
1048
|
-
const hasVIs = props.some(p => {
|
|
1049
|
-
if (p.name !== 'is')
|
|
1050
|
-
return;
|
|
1051
|
-
// v-is="xxx" (TODO: deprecate)
|
|
1052
|
-
if (p.type === 7 /* DIRECTIVE */) {
|
|
1053
|
-
return true;
|
|
1054
|
-
}
|
|
1055
|
-
// is="vue:xxx"
|
|
1056
|
-
if (p.value && p.value.content.startsWith('vue:')) {
|
|
1057
|
-
return true;
|
|
1058
|
-
}
|
|
1059
|
-
// in compat mode, any is usage is considered a component
|
|
1060
|
-
if (checkCompatEnabled("COMPILER_IS_ON_ELEMENT" /* COMPILER_IS_ON_ELEMENT */, context, p.loc)) {
|
|
1061
|
-
return true;
|
|
1062
|
-
}
|
|
1063
|
-
});
|
|
1064
|
-
if (options.isNativeTag && !hasVIs) {
|
|
1065
|
-
if (!options.isNativeTag(tag))
|
|
1066
|
-
tagType = 1 /* COMPONENT */;
|
|
1067
|
-
}
|
|
1068
|
-
else if (hasVIs ||
|
|
1069
|
-
isCoreComponent(tag) ||
|
|
1070
|
-
(options.isBuiltInComponent && options.isBuiltInComponent(tag)) ||
|
|
1071
|
-
/^[A-Z]/.test(tag) ||
|
|
1072
|
-
tag === 'component') {
|
|
1073
|
-
tagType = 1 /* COMPONENT */;
|
|
1074
|
-
}
|
|
1087
|
+
if (!context.inVPre) {
|
|
1075
1088
|
if (tag === 'slot') {
|
|
1076
1089
|
tagType = 2 /* SLOT */;
|
|
1077
1090
|
}
|
|
1078
|
-
else if (tag === 'template'
|
|
1079
|
-
props.some(p => p.type === 7 /* DIRECTIVE */ && isSpecialTemplateDirective(p.name))) {
|
|
1080
|
-
|
|
1091
|
+
else if (tag === 'template') {
|
|
1092
|
+
if (props.some(p => p.type === 7 /* DIRECTIVE */ && isSpecialTemplateDirective(p.name))) {
|
|
1093
|
+
tagType = 3 /* TEMPLATE */;
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
else if (isComponent(tag, props, context)) {
|
|
1097
|
+
tagType = 1 /* COMPONENT */;
|
|
1081
1098
|
}
|
|
1082
1099
|
}
|
|
1083
1100
|
return {
|
|
@@ -1092,6 +1109,49 @@ function parseTag(context, type, parent) {
|
|
|
1092
1109
|
codegenNode: undefined // to be created during transform phase
|
|
1093
1110
|
};
|
|
1094
1111
|
}
|
|
1112
|
+
function isComponent(tag, props, context) {
|
|
1113
|
+
const options = context.options;
|
|
1114
|
+
if (options.isCustomElement(tag)) {
|
|
1115
|
+
return false;
|
|
1116
|
+
}
|
|
1117
|
+
if (tag === 'component' ||
|
|
1118
|
+
/^[A-Z]/.test(tag) ||
|
|
1119
|
+
isCoreComponent(tag) ||
|
|
1120
|
+
(options.isBuiltInComponent && options.isBuiltInComponent(tag)) ||
|
|
1121
|
+
(options.isNativeTag && !options.isNativeTag(tag))) {
|
|
1122
|
+
return true;
|
|
1123
|
+
}
|
|
1124
|
+
// at this point the tag should be a native tag, but check for potential "is"
|
|
1125
|
+
// casting
|
|
1126
|
+
for (let i = 0; i < props.length; i++) {
|
|
1127
|
+
const p = props[i];
|
|
1128
|
+
if (p.type === 6 /* ATTRIBUTE */) {
|
|
1129
|
+
if (p.name === 'is' && p.value) {
|
|
1130
|
+
if (p.value.content.startsWith('vue:')) {
|
|
1131
|
+
return true;
|
|
1132
|
+
}
|
|
1133
|
+
else if (checkCompatEnabled("COMPILER_IS_ON_ELEMENT" /* COMPILER_IS_ON_ELEMENT */, context, p.loc)) {
|
|
1134
|
+
return true;
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
else {
|
|
1139
|
+
// directive
|
|
1140
|
+
// v-is (TODO Deprecate)
|
|
1141
|
+
if (p.name === 'is') {
|
|
1142
|
+
return true;
|
|
1143
|
+
}
|
|
1144
|
+
else if (
|
|
1145
|
+
// :is on plain element - only treat as component in compat mode
|
|
1146
|
+
p.name === 'bind' &&
|
|
1147
|
+
isBindKey(p.arg, 'is') &&
|
|
1148
|
+
true &&
|
|
1149
|
+
checkCompatEnabled("COMPILER_IS_ON_ELEMENT" /* COMPILER_IS_ON_ELEMENT */, context, p.loc)) {
|
|
1150
|
+
return true;
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1095
1155
|
function parseAttributes(context, type) {
|
|
1096
1156
|
const props = [];
|
|
1097
1157
|
const attributeNames = new Set();
|
|
@@ -3968,16 +4028,10 @@ function resolveComponentType(node, context, ssr = false) {
|
|
|
3968
4028
|
let { tag } = node;
|
|
3969
4029
|
// 1. dynamic component
|
|
3970
4030
|
const isExplicitDynamic = isComponentTag(tag);
|
|
3971
|
-
const isProp = findProp(node, 'is')
|
|
4031
|
+
const isProp = findProp(node, 'is');
|
|
3972
4032
|
if (isProp) {
|
|
3973
|
-
if (
|
|
3974
|
-
|
|
3975
|
-
// if not <component>, only is value that starts with "vue:" will be
|
|
3976
|
-
// treated as component by the parse phase and reach here, unless it's
|
|
3977
|
-
// compat mode where all is values are considered components
|
|
3978
|
-
tag = isProp.value.content.replace(/^vue:/, '');
|
|
3979
|
-
}
|
|
3980
|
-
else {
|
|
4033
|
+
if (isExplicitDynamic ||
|
|
4034
|
+
(isCompatEnabled("COMPILER_IS_ON_ELEMENT" /* COMPILER_IS_ON_ELEMENT */, context))) {
|
|
3981
4035
|
const exp = isProp.type === 6 /* ATTRIBUTE */
|
|
3982
4036
|
? isProp.value && createSimpleExpression(isProp.value.content, true)
|
|
3983
4037
|
: isProp.exp;
|
|
@@ -3987,6 +4041,21 @@ function resolveComponentType(node, context, ssr = false) {
|
|
|
3987
4041
|
]);
|
|
3988
4042
|
}
|
|
3989
4043
|
}
|
|
4044
|
+
else if (isProp.type === 6 /* ATTRIBUTE */ &&
|
|
4045
|
+
isProp.value.content.startsWith('vue:')) {
|
|
4046
|
+
// <button is="vue:xxx">
|
|
4047
|
+
// if not <component>, only is value that starts with "vue:" will be
|
|
4048
|
+
// treated as component by the parse phase and reach here, unless it's
|
|
4049
|
+
// compat mode where all is values are considered components
|
|
4050
|
+
tag = isProp.value.content.slice(4);
|
|
4051
|
+
}
|
|
4052
|
+
}
|
|
4053
|
+
// 1.5 v-is (TODO: Deprecate)
|
|
4054
|
+
const isDir = !isExplicitDynamic && findDir(node, 'is');
|
|
4055
|
+
if (isDir && isDir.exp) {
|
|
4056
|
+
return createCallExpression(context.helper(RESOLVE_DYNAMIC_COMPONENT), [
|
|
4057
|
+
isDir.exp
|
|
4058
|
+
]);
|
|
3990
4059
|
}
|
|
3991
4060
|
// 2. built-in components (Teleport, Transition, KeepAlive, Suspense...)
|
|
3992
4061
|
const builtIn = isCoreComponent(tag) || context.isBuiltInComponent(tag);
|
|
@@ -4130,7 +4199,9 @@ function buildProps(node, context, props = node.props, ssr = false) {
|
|
|
4130
4199
|
}
|
|
4131
4200
|
// skip is on <component>, or is="vue:xxx"
|
|
4132
4201
|
if (name === 'is' &&
|
|
4133
|
-
(isComponentTag(tag) ||
|
|
4202
|
+
(isComponentTag(tag) ||
|
|
4203
|
+
(value && value.content.startsWith('vue:')) ||
|
|
4204
|
+
(isCompatEnabled("COMPILER_IS_ON_ELEMENT" /* COMPILER_IS_ON_ELEMENT */, context)))) {
|
|
4134
4205
|
continue;
|
|
4135
4206
|
}
|
|
4136
4207
|
properties.push(createObjectProperty(createSimpleExpression(name, true, getInnerRange(loc, 0, name.length)), createSimpleExpression(value ? value.content : '', isStatic, value ? value.loc : loc)));
|
|
@@ -4153,7 +4224,10 @@ function buildProps(node, context, props = node.props, ssr = false) {
|
|
|
4153
4224
|
}
|
|
4154
4225
|
// skip v-is and :is on <component>
|
|
4155
4226
|
if (name === 'is' ||
|
|
4156
|
-
(isVBind &&
|
|
4227
|
+
(isVBind &&
|
|
4228
|
+
isBindKey(arg, 'is') &&
|
|
4229
|
+
(isComponentTag(tag) ||
|
|
4230
|
+
(isCompatEnabled("COMPILER_IS_ON_ELEMENT" /* COMPILER_IS_ON_ELEMENT */, context))))) {
|
|
4157
4231
|
continue;
|
|
4158
4232
|
}
|
|
4159
4233
|
// skip v-on in SSR compilation
|
|
@@ -4737,7 +4811,7 @@ const transformModel = (dir, node, context) => {
|
|
|
4737
4811
|
const maybeRef = context.inline &&
|
|
4738
4812
|
bindingType &&
|
|
4739
4813
|
bindingType !== "setup-const" /* SETUP_CONST */;
|
|
4740
|
-
if (!isMemberExpression(expString) && !maybeRef) {
|
|
4814
|
+
if (!expString.trim() || (!isMemberExpression(expString) && !maybeRef)) {
|
|
4741
4815
|
context.onError(createCompilerError(41 /* X_V_MODEL_MALFORMED_EXPRESSION */, exp.loc));
|
|
4742
4816
|
return createTransformProps();
|
|
4743
4817
|
}
|
|
@@ -355,18 +355,59 @@ function isCoreComponent(tag) {
|
|
|
355
355
|
}
|
|
356
356
|
const nonIdentifierRE = /^\d|[^\$\w]/;
|
|
357
357
|
const isSimpleIdentifier = (name) => !nonIdentifierRE.test(name);
|
|
358
|
-
const
|
|
358
|
+
const validFirstIdentCharRE = /[A-Za-z_$\xA0-\uFFFF]/;
|
|
359
|
+
const validIdentCharRE = /[\.\w$\xA0-\uFFFF]/;
|
|
360
|
+
const whitespaceRE = /\s+[.[]\s*|\s*[.[]\s+/g;
|
|
361
|
+
/**
|
|
362
|
+
* Simple lexer to check if an expression is a member expression. This is
|
|
363
|
+
* lax and only checks validity at the root level (i.e. does not validate exps
|
|
364
|
+
* inside square brackets), but it's ok since these are only used on template
|
|
365
|
+
* expressions and false positives are invalid expressions in the first place.
|
|
366
|
+
*/
|
|
359
367
|
const isMemberExpression = (path) => {
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
368
|
+
// remove whitespaces around . or [ first
|
|
369
|
+
path = path.trim().replace(whitespaceRE, s => s.trim());
|
|
370
|
+
let state = 0 /* inMemberExp */;
|
|
371
|
+
let prevState = 0 /* inMemberExp */;
|
|
372
|
+
let currentOpenBracketCount = 0;
|
|
373
|
+
let currentStringType = null;
|
|
374
|
+
for (let i = 0; i < path.length; i++) {
|
|
375
|
+
const char = path.charAt(i);
|
|
376
|
+
switch (state) {
|
|
377
|
+
case 0 /* inMemberExp */:
|
|
378
|
+
if (char === '[') {
|
|
379
|
+
prevState = state;
|
|
380
|
+
state = 1 /* inBrackets */;
|
|
381
|
+
currentOpenBracketCount++;
|
|
382
|
+
}
|
|
383
|
+
else if (!(i === 0 ? validFirstIdentCharRE : validIdentCharRE).test(char)) {
|
|
384
|
+
return false;
|
|
385
|
+
}
|
|
386
|
+
break;
|
|
387
|
+
case 1 /* inBrackets */:
|
|
388
|
+
if (char === `'` || char === `"` || char === '`') {
|
|
389
|
+
prevState = state;
|
|
390
|
+
state = 2 /* inString */;
|
|
391
|
+
currentStringType = char;
|
|
392
|
+
}
|
|
393
|
+
else if (char === `[`) {
|
|
394
|
+
currentOpenBracketCount++;
|
|
395
|
+
}
|
|
396
|
+
else if (char === `]`) {
|
|
397
|
+
if (!--currentOpenBracketCount) {
|
|
398
|
+
state = prevState;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
break;
|
|
402
|
+
case 2 /* inString */:
|
|
403
|
+
if (char === currentStringType) {
|
|
404
|
+
state = prevState;
|
|
405
|
+
currentStringType = null;
|
|
406
|
+
}
|
|
407
|
+
break;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
return !currentOpenBracketCount;
|
|
370
411
|
};
|
|
371
412
|
function getInnerRange(loc, offset, length) {
|
|
372
413
|
const source = loc.source.substr(offset, length);
|
|
@@ -1025,41 +1066,17 @@ function parseTag(context, type, parent) {
|
|
|
1025
1066
|
return;
|
|
1026
1067
|
}
|
|
1027
1068
|
let tagType = 0 /* ELEMENT */;
|
|
1028
|
-
|
|
1029
|
-
if (!context.inVPre && !options.isCustomElement(tag)) {
|
|
1030
|
-
const hasVIs = props.some(p => {
|
|
1031
|
-
if (p.name !== 'is')
|
|
1032
|
-
return;
|
|
1033
|
-
// v-is="xxx" (TODO: deprecate)
|
|
1034
|
-
if (p.type === 7 /* DIRECTIVE */) {
|
|
1035
|
-
return true;
|
|
1036
|
-
}
|
|
1037
|
-
// is="vue:xxx"
|
|
1038
|
-
if (p.value && p.value.content.startsWith('vue:')) {
|
|
1039
|
-
return true;
|
|
1040
|
-
}
|
|
1041
|
-
// in compat mode, any is usage is considered a component
|
|
1042
|
-
if (checkCompatEnabled("COMPILER_IS_ON_ELEMENT" /* COMPILER_IS_ON_ELEMENT */, context, p.loc)) {
|
|
1043
|
-
return true;
|
|
1044
|
-
}
|
|
1045
|
-
});
|
|
1046
|
-
if (options.isNativeTag && !hasVIs) {
|
|
1047
|
-
if (!options.isNativeTag(tag))
|
|
1048
|
-
tagType = 1 /* COMPONENT */;
|
|
1049
|
-
}
|
|
1050
|
-
else if (hasVIs ||
|
|
1051
|
-
isCoreComponent(tag) ||
|
|
1052
|
-
(options.isBuiltInComponent && options.isBuiltInComponent(tag)) ||
|
|
1053
|
-
/^[A-Z]/.test(tag) ||
|
|
1054
|
-
tag === 'component') {
|
|
1055
|
-
tagType = 1 /* COMPONENT */;
|
|
1056
|
-
}
|
|
1069
|
+
if (!context.inVPre) {
|
|
1057
1070
|
if (tag === 'slot') {
|
|
1058
1071
|
tagType = 2 /* SLOT */;
|
|
1059
1072
|
}
|
|
1060
|
-
else if (tag === 'template'
|
|
1061
|
-
props.some(p => p.type === 7 /* DIRECTIVE */ && isSpecialTemplateDirective(p.name))) {
|
|
1062
|
-
|
|
1073
|
+
else if (tag === 'template') {
|
|
1074
|
+
if (props.some(p => p.type === 7 /* DIRECTIVE */ && isSpecialTemplateDirective(p.name))) {
|
|
1075
|
+
tagType = 3 /* TEMPLATE */;
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
else if (isComponent(tag, props, context)) {
|
|
1079
|
+
tagType = 1 /* COMPONENT */;
|
|
1063
1080
|
}
|
|
1064
1081
|
}
|
|
1065
1082
|
return {
|
|
@@ -1074,6 +1091,49 @@ function parseTag(context, type, parent) {
|
|
|
1074
1091
|
codegenNode: undefined // to be created during transform phase
|
|
1075
1092
|
};
|
|
1076
1093
|
}
|
|
1094
|
+
function isComponent(tag, props, context) {
|
|
1095
|
+
const options = context.options;
|
|
1096
|
+
if (options.isCustomElement(tag)) {
|
|
1097
|
+
return false;
|
|
1098
|
+
}
|
|
1099
|
+
if (tag === 'component' ||
|
|
1100
|
+
/^[A-Z]/.test(tag) ||
|
|
1101
|
+
isCoreComponent(tag) ||
|
|
1102
|
+
(options.isBuiltInComponent && options.isBuiltInComponent(tag)) ||
|
|
1103
|
+
(options.isNativeTag && !options.isNativeTag(tag))) {
|
|
1104
|
+
return true;
|
|
1105
|
+
}
|
|
1106
|
+
// at this point the tag should be a native tag, but check for potential "is"
|
|
1107
|
+
// casting
|
|
1108
|
+
for (let i = 0; i < props.length; i++) {
|
|
1109
|
+
const p = props[i];
|
|
1110
|
+
if (p.type === 6 /* ATTRIBUTE */) {
|
|
1111
|
+
if (p.name === 'is' && p.value) {
|
|
1112
|
+
if (p.value.content.startsWith('vue:')) {
|
|
1113
|
+
return true;
|
|
1114
|
+
}
|
|
1115
|
+
else if (checkCompatEnabled("COMPILER_IS_ON_ELEMENT" /* COMPILER_IS_ON_ELEMENT */, context, p.loc)) {
|
|
1116
|
+
return true;
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
else {
|
|
1121
|
+
// directive
|
|
1122
|
+
// v-is (TODO Deprecate)
|
|
1123
|
+
if (p.name === 'is') {
|
|
1124
|
+
return true;
|
|
1125
|
+
}
|
|
1126
|
+
else if (
|
|
1127
|
+
// :is on plain element - only treat as component in compat mode
|
|
1128
|
+
p.name === 'bind' &&
|
|
1129
|
+
isBindKey(p.arg, 'is') &&
|
|
1130
|
+
true &&
|
|
1131
|
+
checkCompatEnabled("COMPILER_IS_ON_ELEMENT" /* COMPILER_IS_ON_ELEMENT */, context, p.loc)) {
|
|
1132
|
+
return true;
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1077
1137
|
function parseAttributes(context, type) {
|
|
1078
1138
|
const props = [];
|
|
1079
1139
|
const attributeNames = new Set();
|
|
@@ -3873,16 +3933,10 @@ function resolveComponentType(node, context, ssr = false) {
|
|
|
3873
3933
|
let { tag } = node;
|
|
3874
3934
|
// 1. dynamic component
|
|
3875
3935
|
const isExplicitDynamic = isComponentTag(tag);
|
|
3876
|
-
const isProp = findProp(node, 'is')
|
|
3936
|
+
const isProp = findProp(node, 'is');
|
|
3877
3937
|
if (isProp) {
|
|
3878
|
-
if (
|
|
3879
|
-
|
|
3880
|
-
// if not <component>, only is value that starts with "vue:" will be
|
|
3881
|
-
// treated as component by the parse phase and reach here, unless it's
|
|
3882
|
-
// compat mode where all is values are considered components
|
|
3883
|
-
tag = isProp.value.content.replace(/^vue:/, '');
|
|
3884
|
-
}
|
|
3885
|
-
else {
|
|
3938
|
+
if (isExplicitDynamic ||
|
|
3939
|
+
(isCompatEnabled("COMPILER_IS_ON_ELEMENT" /* COMPILER_IS_ON_ELEMENT */, context))) {
|
|
3886
3940
|
const exp = isProp.type === 6 /* ATTRIBUTE */
|
|
3887
3941
|
? isProp.value && createSimpleExpression(isProp.value.content, true)
|
|
3888
3942
|
: isProp.exp;
|
|
@@ -3892,6 +3946,21 @@ function resolveComponentType(node, context, ssr = false) {
|
|
|
3892
3946
|
]);
|
|
3893
3947
|
}
|
|
3894
3948
|
}
|
|
3949
|
+
else if (isProp.type === 6 /* ATTRIBUTE */ &&
|
|
3950
|
+
isProp.value.content.startsWith('vue:')) {
|
|
3951
|
+
// <button is="vue:xxx">
|
|
3952
|
+
// if not <component>, only is value that starts with "vue:" will be
|
|
3953
|
+
// treated as component by the parse phase and reach here, unless it's
|
|
3954
|
+
// compat mode where all is values are considered components
|
|
3955
|
+
tag = isProp.value.content.slice(4);
|
|
3956
|
+
}
|
|
3957
|
+
}
|
|
3958
|
+
// 1.5 v-is (TODO: Deprecate)
|
|
3959
|
+
const isDir = !isExplicitDynamic && findDir(node, 'is');
|
|
3960
|
+
if (isDir && isDir.exp) {
|
|
3961
|
+
return createCallExpression(context.helper(RESOLVE_DYNAMIC_COMPONENT), [
|
|
3962
|
+
isDir.exp
|
|
3963
|
+
]);
|
|
3895
3964
|
}
|
|
3896
3965
|
// 2. built-in components (Teleport, Transition, KeepAlive, Suspense...)
|
|
3897
3966
|
const builtIn = isCoreComponent(tag) || context.isBuiltInComponent(tag);
|
|
@@ -4035,7 +4104,9 @@ function buildProps(node, context, props = node.props, ssr = false) {
|
|
|
4035
4104
|
}
|
|
4036
4105
|
// skip is on <component>, or is="vue:xxx"
|
|
4037
4106
|
if (name === 'is' &&
|
|
4038
|
-
(isComponentTag(tag) ||
|
|
4107
|
+
(isComponentTag(tag) ||
|
|
4108
|
+
(value && value.content.startsWith('vue:')) ||
|
|
4109
|
+
(isCompatEnabled("COMPILER_IS_ON_ELEMENT" /* COMPILER_IS_ON_ELEMENT */, context)))) {
|
|
4039
4110
|
continue;
|
|
4040
4111
|
}
|
|
4041
4112
|
properties.push(createObjectProperty(createSimpleExpression(name, true, getInnerRange(loc, 0, name.length)), createSimpleExpression(value ? value.content : '', isStatic, value ? value.loc : loc)));
|
|
@@ -4058,7 +4129,10 @@ function buildProps(node, context, props = node.props, ssr = false) {
|
|
|
4058
4129
|
}
|
|
4059
4130
|
// skip v-is and :is on <component>
|
|
4060
4131
|
if (name === 'is' ||
|
|
4061
|
-
(isVBind &&
|
|
4132
|
+
(isVBind &&
|
|
4133
|
+
isBindKey(arg, 'is') &&
|
|
4134
|
+
(isComponentTag(tag) ||
|
|
4135
|
+
(isCompatEnabled("COMPILER_IS_ON_ELEMENT" /* COMPILER_IS_ON_ELEMENT */, context))))) {
|
|
4062
4136
|
continue;
|
|
4063
4137
|
}
|
|
4064
4138
|
// skip v-on in SSR compilation
|
|
@@ -4616,7 +4690,7 @@ const transformModel = (dir, node, context) => {
|
|
|
4616
4690
|
const maybeRef = context.inline &&
|
|
4617
4691
|
bindingType &&
|
|
4618
4692
|
bindingType !== "setup-const" /* SETUP_CONST */;
|
|
4619
|
-
if (!isMemberExpression(expString) && !maybeRef) {
|
|
4693
|
+
if (!expString.trim() || (!isMemberExpression(expString) && !maybeRef)) {
|
|
4620
4694
|
context.onError(createCompilerError(41 /* X_V_MODEL_MALFORMED_EXPRESSION */, exp.loc));
|
|
4621
4695
|
return createTransformProps();
|
|
4622
4696
|
}
|
package/dist/compiler-core.d.ts
CHANGED
|
@@ -557,6 +557,12 @@ export declare const isBuiltInType: (tag: string, expected: string) => boolean;
|
|
|
557
557
|
|
|
558
558
|
export declare function isCoreComponent(tag: string): symbol | void;
|
|
559
559
|
|
|
560
|
+
/**
|
|
561
|
+
* Simple lexer to check if an expression is a member expression. This is
|
|
562
|
+
* lax and only checks validity at the root level (i.e. does not validate exps
|
|
563
|
+
* inside square brackets), but it's ok since these are only used on template
|
|
564
|
+
* expressions and false positives are invalid expressions in the first place.
|
|
565
|
+
*/
|
|
560
566
|
export declare const isMemberExpression: (path: string) => boolean;
|
|
561
567
|
|
|
562
568
|
export declare const isSimpleIdentifier: (name: string) => boolean;
|
|
@@ -351,18 +351,59 @@ function isCoreComponent(tag) {
|
|
|
351
351
|
}
|
|
352
352
|
const nonIdentifierRE = /^\d|[^\$\w]/;
|
|
353
353
|
const isSimpleIdentifier = (name) => !nonIdentifierRE.test(name);
|
|
354
|
-
const
|
|
354
|
+
const validFirstIdentCharRE = /[A-Za-z_$\xA0-\uFFFF]/;
|
|
355
|
+
const validIdentCharRE = /[\.\w$\xA0-\uFFFF]/;
|
|
356
|
+
const whitespaceRE = /\s+[.[]\s*|\s*[.[]\s+/g;
|
|
357
|
+
/**
|
|
358
|
+
* Simple lexer to check if an expression is a member expression. This is
|
|
359
|
+
* lax and only checks validity at the root level (i.e. does not validate exps
|
|
360
|
+
* inside square brackets), but it's ok since these are only used on template
|
|
361
|
+
* expressions and false positives are invalid expressions in the first place.
|
|
362
|
+
*/
|
|
355
363
|
const isMemberExpression = (path) => {
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
364
|
+
// remove whitespaces around . or [ first
|
|
365
|
+
path = path.trim().replace(whitespaceRE, s => s.trim());
|
|
366
|
+
let state = 0 /* inMemberExp */;
|
|
367
|
+
let prevState = 0 /* inMemberExp */;
|
|
368
|
+
let currentOpenBracketCount = 0;
|
|
369
|
+
let currentStringType = null;
|
|
370
|
+
for (let i = 0; i < path.length; i++) {
|
|
371
|
+
const char = path.charAt(i);
|
|
372
|
+
switch (state) {
|
|
373
|
+
case 0 /* inMemberExp */:
|
|
374
|
+
if (char === '[') {
|
|
375
|
+
prevState = state;
|
|
376
|
+
state = 1 /* inBrackets */;
|
|
377
|
+
currentOpenBracketCount++;
|
|
378
|
+
}
|
|
379
|
+
else if (!(i === 0 ? validFirstIdentCharRE : validIdentCharRE).test(char)) {
|
|
380
|
+
return false;
|
|
381
|
+
}
|
|
382
|
+
break;
|
|
383
|
+
case 1 /* inBrackets */:
|
|
384
|
+
if (char === `'` || char === `"` || char === '`') {
|
|
385
|
+
prevState = state;
|
|
386
|
+
state = 2 /* inString */;
|
|
387
|
+
currentStringType = char;
|
|
388
|
+
}
|
|
389
|
+
else if (char === `[`) {
|
|
390
|
+
currentOpenBracketCount++;
|
|
391
|
+
}
|
|
392
|
+
else if (char === `]`) {
|
|
393
|
+
if (!--currentOpenBracketCount) {
|
|
394
|
+
state = prevState;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
break;
|
|
398
|
+
case 2 /* inString */:
|
|
399
|
+
if (char === currentStringType) {
|
|
400
|
+
state = prevState;
|
|
401
|
+
currentStringType = null;
|
|
402
|
+
}
|
|
403
|
+
break;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
return !currentOpenBracketCount;
|
|
366
407
|
};
|
|
367
408
|
function getInnerRange(loc, offset, length) {
|
|
368
409
|
const source = loc.source.substr(offset, length);
|
|
@@ -1048,41 +1089,17 @@ function parseTag(context, type, parent) {
|
|
|
1048
1089
|
}
|
|
1049
1090
|
}
|
|
1050
1091
|
let tagType = 0 /* ELEMENT */;
|
|
1051
|
-
|
|
1052
|
-
if (!context.inVPre && !options.isCustomElement(tag)) {
|
|
1053
|
-
const hasVIs = props.some(p => {
|
|
1054
|
-
if (p.name !== 'is')
|
|
1055
|
-
return;
|
|
1056
|
-
// v-is="xxx" (TODO: deprecate)
|
|
1057
|
-
if (p.type === 7 /* DIRECTIVE */) {
|
|
1058
|
-
return true;
|
|
1059
|
-
}
|
|
1060
|
-
// is="vue:xxx"
|
|
1061
|
-
if (p.value && p.value.content.startsWith('vue:')) {
|
|
1062
|
-
return true;
|
|
1063
|
-
}
|
|
1064
|
-
// in compat mode, any is usage is considered a component
|
|
1065
|
-
if (checkCompatEnabled("COMPILER_IS_ON_ELEMENT" /* COMPILER_IS_ON_ELEMENT */, context, p.loc)) {
|
|
1066
|
-
return true;
|
|
1067
|
-
}
|
|
1068
|
-
});
|
|
1069
|
-
if (options.isNativeTag && !hasVIs) {
|
|
1070
|
-
if (!options.isNativeTag(tag))
|
|
1071
|
-
tagType = 1 /* COMPONENT */;
|
|
1072
|
-
}
|
|
1073
|
-
else if (hasVIs ||
|
|
1074
|
-
isCoreComponent(tag) ||
|
|
1075
|
-
(options.isBuiltInComponent && options.isBuiltInComponent(tag)) ||
|
|
1076
|
-
/^[A-Z]/.test(tag) ||
|
|
1077
|
-
tag === 'component') {
|
|
1078
|
-
tagType = 1 /* COMPONENT */;
|
|
1079
|
-
}
|
|
1092
|
+
if (!context.inVPre) {
|
|
1080
1093
|
if (tag === 'slot') {
|
|
1081
1094
|
tagType = 2 /* SLOT */;
|
|
1082
1095
|
}
|
|
1083
|
-
else if (tag === 'template'
|
|
1084
|
-
props.some(p => p.type === 7 /* DIRECTIVE */ && isSpecialTemplateDirective(p.name))) {
|
|
1085
|
-
|
|
1096
|
+
else if (tag === 'template') {
|
|
1097
|
+
if (props.some(p => p.type === 7 /* DIRECTIVE */ && isSpecialTemplateDirective(p.name))) {
|
|
1098
|
+
tagType = 3 /* TEMPLATE */;
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
else if (isComponent(tag, props, context)) {
|
|
1102
|
+
tagType = 1 /* COMPONENT */;
|
|
1086
1103
|
}
|
|
1087
1104
|
}
|
|
1088
1105
|
return {
|
|
@@ -1097,6 +1114,49 @@ function parseTag(context, type, parent) {
|
|
|
1097
1114
|
codegenNode: undefined // to be created during transform phase
|
|
1098
1115
|
};
|
|
1099
1116
|
}
|
|
1117
|
+
function isComponent(tag, props, context) {
|
|
1118
|
+
const options = context.options;
|
|
1119
|
+
if (options.isCustomElement(tag)) {
|
|
1120
|
+
return false;
|
|
1121
|
+
}
|
|
1122
|
+
if (tag === 'component' ||
|
|
1123
|
+
/^[A-Z]/.test(tag) ||
|
|
1124
|
+
isCoreComponent(tag) ||
|
|
1125
|
+
(options.isBuiltInComponent && options.isBuiltInComponent(tag)) ||
|
|
1126
|
+
(options.isNativeTag && !options.isNativeTag(tag))) {
|
|
1127
|
+
return true;
|
|
1128
|
+
}
|
|
1129
|
+
// at this point the tag should be a native tag, but check for potential "is"
|
|
1130
|
+
// casting
|
|
1131
|
+
for (let i = 0; i < props.length; i++) {
|
|
1132
|
+
const p = props[i];
|
|
1133
|
+
if (p.type === 6 /* ATTRIBUTE */) {
|
|
1134
|
+
if (p.name === 'is' && p.value) {
|
|
1135
|
+
if (p.value.content.startsWith('vue:')) {
|
|
1136
|
+
return true;
|
|
1137
|
+
}
|
|
1138
|
+
else if (checkCompatEnabled("COMPILER_IS_ON_ELEMENT" /* COMPILER_IS_ON_ELEMENT */, context, p.loc)) {
|
|
1139
|
+
return true;
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
else {
|
|
1144
|
+
// directive
|
|
1145
|
+
// v-is (TODO Deprecate)
|
|
1146
|
+
if (p.name === 'is') {
|
|
1147
|
+
return true;
|
|
1148
|
+
}
|
|
1149
|
+
else if (
|
|
1150
|
+
// :is on plain element - only treat as component in compat mode
|
|
1151
|
+
p.name === 'bind' &&
|
|
1152
|
+
isBindKey(p.arg, 'is') &&
|
|
1153
|
+
true &&
|
|
1154
|
+
checkCompatEnabled("COMPILER_IS_ON_ELEMENT" /* COMPILER_IS_ON_ELEMENT */, context, p.loc)) {
|
|
1155
|
+
return true;
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1100
1160
|
function parseAttributes(context, type) {
|
|
1101
1161
|
const props = [];
|
|
1102
1162
|
const attributeNames = new Set();
|
|
@@ -3420,16 +3480,10 @@ function resolveComponentType(node, context, ssr = false) {
|
|
|
3420
3480
|
let { tag } = node;
|
|
3421
3481
|
// 1. dynamic component
|
|
3422
3482
|
const isExplicitDynamic = isComponentTag(tag);
|
|
3423
|
-
const isProp = findProp(node, 'is')
|
|
3483
|
+
const isProp = findProp(node, 'is');
|
|
3424
3484
|
if (isProp) {
|
|
3425
|
-
if (
|
|
3426
|
-
|
|
3427
|
-
// if not <component>, only is value that starts with "vue:" will be
|
|
3428
|
-
// treated as component by the parse phase and reach here, unless it's
|
|
3429
|
-
// compat mode where all is values are considered components
|
|
3430
|
-
tag = isProp.value.content.replace(/^vue:/, '');
|
|
3431
|
-
}
|
|
3432
|
-
else {
|
|
3485
|
+
if (isExplicitDynamic ||
|
|
3486
|
+
(isCompatEnabled("COMPILER_IS_ON_ELEMENT" /* COMPILER_IS_ON_ELEMENT */, context))) {
|
|
3433
3487
|
const exp = isProp.type === 6 /* ATTRIBUTE */
|
|
3434
3488
|
? isProp.value && createSimpleExpression(isProp.value.content, true)
|
|
3435
3489
|
: isProp.exp;
|
|
@@ -3439,6 +3493,21 @@ function resolveComponentType(node, context, ssr = false) {
|
|
|
3439
3493
|
]);
|
|
3440
3494
|
}
|
|
3441
3495
|
}
|
|
3496
|
+
else if (isProp.type === 6 /* ATTRIBUTE */ &&
|
|
3497
|
+
isProp.value.content.startsWith('vue:')) {
|
|
3498
|
+
// <button is="vue:xxx">
|
|
3499
|
+
// if not <component>, only is value that starts with "vue:" will be
|
|
3500
|
+
// treated as component by the parse phase and reach here, unless it's
|
|
3501
|
+
// compat mode where all is values are considered components
|
|
3502
|
+
tag = isProp.value.content.slice(4);
|
|
3503
|
+
}
|
|
3504
|
+
}
|
|
3505
|
+
// 1.5 v-is (TODO: Deprecate)
|
|
3506
|
+
const isDir = !isExplicitDynamic && findDir(node, 'is');
|
|
3507
|
+
if (isDir && isDir.exp) {
|
|
3508
|
+
return createCallExpression(context.helper(RESOLVE_DYNAMIC_COMPONENT), [
|
|
3509
|
+
isDir.exp
|
|
3510
|
+
]);
|
|
3442
3511
|
}
|
|
3443
3512
|
// 2. built-in components (Teleport, Transition, KeepAlive, Suspense...)
|
|
3444
3513
|
const builtIn = isCoreComponent(tag) || context.isBuiltInComponent(tag);
|
|
@@ -3522,7 +3591,9 @@ function buildProps(node, context, props = node.props, ssr = false) {
|
|
|
3522
3591
|
}
|
|
3523
3592
|
// skip is on <component>, or is="vue:xxx"
|
|
3524
3593
|
if (name === 'is' &&
|
|
3525
|
-
(isComponentTag(tag) ||
|
|
3594
|
+
(isComponentTag(tag) ||
|
|
3595
|
+
(value && value.content.startsWith('vue:')) ||
|
|
3596
|
+
(isCompatEnabled("COMPILER_IS_ON_ELEMENT" /* COMPILER_IS_ON_ELEMENT */, context)))) {
|
|
3526
3597
|
continue;
|
|
3527
3598
|
}
|
|
3528
3599
|
properties.push(createObjectProperty(createSimpleExpression(name, true, getInnerRange(loc, 0, name.length)), createSimpleExpression(value ? value.content : '', isStatic, value ? value.loc : loc)));
|
|
@@ -3545,7 +3616,10 @@ function buildProps(node, context, props = node.props, ssr = false) {
|
|
|
3545
3616
|
}
|
|
3546
3617
|
// skip v-is and :is on <component>
|
|
3547
3618
|
if (name === 'is' ||
|
|
3548
|
-
(isVBind &&
|
|
3619
|
+
(isVBind &&
|
|
3620
|
+
isBindKey(arg, 'is') &&
|
|
3621
|
+
(isComponentTag(tag) ||
|
|
3622
|
+
(isCompatEnabled("COMPILER_IS_ON_ELEMENT" /* COMPILER_IS_ON_ELEMENT */, context))))) {
|
|
3549
3623
|
continue;
|
|
3550
3624
|
}
|
|
3551
3625
|
// skip v-on in SSR compilation
|
|
@@ -4089,7 +4163,7 @@ const transformModel = (dir, node, context) => {
|
|
|
4089
4163
|
// _unref(exp)
|
|
4090
4164
|
context.bindingMetadata[rawExp];
|
|
4091
4165
|
const maybeRef = !true /* SETUP_CONST */;
|
|
4092
|
-
if (!isMemberExpression(expString) && !maybeRef) {
|
|
4166
|
+
if (!expString.trim() || (!isMemberExpression(expString) && !maybeRef)) {
|
|
4093
4167
|
context.onError(createCompilerError(41 /* X_V_MODEL_MALFORMED_EXPRESSION */, exp.loc));
|
|
4094
4168
|
return createTransformProps();
|
|
4095
4169
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vue/compiler-core",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.2",
|
|
4
4
|
"description": "@vue/compiler-core",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"module": "dist/compiler-core.esm-bundler.js",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
},
|
|
33
33
|
"homepage": "https://github.com/vuejs/vue-next/tree/master/packages/compiler-core#readme",
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@vue/shared": "3.1.
|
|
35
|
+
"@vue/shared": "3.1.2",
|
|
36
36
|
"@babel/parser": "^7.12.0",
|
|
37
37
|
"@babel/types": "^7.12.0",
|
|
38
38
|
"estree-walker": "^2.0.1",
|