@vue/compiler-core 3.1.1 → 3.1.5

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.
@@ -356,18 +356,84 @@ function isCoreComponent(tag) {
356
356
  }
357
357
  const nonIdentifierRE = /^\d|[^\$\w]/;
358
358
  const isSimpleIdentifier = (name) => !nonIdentifierRE.test(name);
359
- const memberExpRE = /^[A-Za-z_$\xA0-\uFFFF][\w$\xA0-\uFFFF]*(?:\s*\.\s*[A-Za-z_$\xA0-\uFFFF][\w$\xA0-\uFFFF]*|\[(.+)\])*$/;
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
- if (!path)
362
- return false;
363
- const matched = memberExpRE.exec(path.trim());
364
- if (!matched)
365
- return false;
366
- if (!matched[1])
367
- return true;
368
- if (!/[\[\]]/.test(matched[1]))
369
- return true;
370
- return isMemberExpression(matched[1].trim());
369
+ // remove whitespaces around . or [ first
370
+ path = path.trim().replace(whitespaceRE, s => s.trim());
371
+ let state = 0 /* inMemberExp */;
372
+ let stateStack = [];
373
+ let currentOpenBracketCount = 0;
374
+ let currentOpenParensCount = 0;
375
+ let currentStringType = null;
376
+ for (let i = 0; i < path.length; i++) {
377
+ const char = path.charAt(i);
378
+ switch (state) {
379
+ case 0 /* inMemberExp */:
380
+ if (char === '[') {
381
+ stateStack.push(state);
382
+ state = 1 /* inBrackets */;
383
+ currentOpenBracketCount++;
384
+ }
385
+ else if (char === '(') {
386
+ stateStack.push(state);
387
+ state = 2 /* inParens */;
388
+ currentOpenParensCount++;
389
+ }
390
+ else if (!(i === 0 ? validFirstIdentCharRE : validIdentCharRE).test(char)) {
391
+ return false;
392
+ }
393
+ break;
394
+ case 1 /* inBrackets */:
395
+ if (char === `'` || char === `"` || char === '`') {
396
+ stateStack.push(state);
397
+ state = 3 /* inString */;
398
+ currentStringType = char;
399
+ }
400
+ else if (char === `[`) {
401
+ currentOpenBracketCount++;
402
+ }
403
+ else if (char === `]`) {
404
+ if (!--currentOpenBracketCount) {
405
+ state = stateStack.pop();
406
+ }
407
+ }
408
+ break;
409
+ case 2 /* inParens */:
410
+ if (char === `'` || char === `"` || char === '`') {
411
+ stateStack.push(state);
412
+ state = 3 /* inString */;
413
+ currentStringType = char;
414
+ }
415
+ else if (char === `(`) {
416
+ currentOpenParensCount++;
417
+ }
418
+ else if (char === `)`) {
419
+ // if the exp ends as a call then it should not be considered valid
420
+ if (i === path.length - 1) {
421
+ return false;
422
+ }
423
+ if (!--currentOpenParensCount) {
424
+ state = stateStack.pop();
425
+ }
426
+ }
427
+ break;
428
+ case 3 /* inString */:
429
+ if (char === currentStringType) {
430
+ state = stateStack.pop();
431
+ currentStringType = null;
432
+ }
433
+ break;
434
+ }
435
+ }
436
+ return !currentOpenBracketCount && !currentOpenParensCount;
371
437
  };
372
438
  function getInnerRange(loc, offset, length) {
373
439
  const source = loc.source.substr(offset, length);
@@ -936,6 +1002,10 @@ function parseElement(context, ancestors) {
936
1002
  const isPreBoundary = context.inPre && !wasInPre;
937
1003
  const isVPreBoundary = context.inVPre && !wasInVPre;
938
1004
  if (element.isSelfClosing || context.options.isVoidTag(element.tag)) {
1005
+ // #4030 self-closing <pre> tag
1006
+ if (context.options.isPreTag(element.tag)) {
1007
+ context.inPre = false;
1008
+ }
939
1009
  return element;
940
1010
  }
941
1011
  // Children.
@@ -991,12 +1061,13 @@ function parseTag(context, type, parent) {
991
1061
  // save current state in case we need to re-parse attributes with v-pre
992
1062
  const cursor = getCursor(context);
993
1063
  const currentSource = context.source;
994
- // Attributes.
995
- let props = parseAttributes(context, type);
996
1064
  // check <pre> tag
997
- if (context.options.isPreTag(tag)) {
1065
+ const isPreTag = context.options.isPreTag(tag);
1066
+ if (isPreTag) {
998
1067
  context.inPre = true;
999
1068
  }
1069
+ // Attributes.
1070
+ let props = parseAttributes(context, type);
1000
1071
  // check v-pre
1001
1072
  if (type === 0 /* Start */ &&
1002
1073
  !context.inVPre &&
@@ -1043,41 +1114,17 @@ function parseTag(context, type, parent) {
1043
1114
  }
1044
1115
  }
1045
1116
  let tagType = 0 /* ELEMENT */;
1046
- const options = context.options;
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
- }
1117
+ if (!context.inVPre) {
1075
1118
  if (tag === 'slot') {
1076
1119
  tagType = 2 /* SLOT */;
1077
1120
  }
1078
- else if (tag === 'template' &&
1079
- props.some(p => p.type === 7 /* DIRECTIVE */ && isSpecialTemplateDirective(p.name))) {
1080
- tagType = 3 /* TEMPLATE */;
1121
+ else if (tag === 'template') {
1122
+ if (props.some(p => p.type === 7 /* DIRECTIVE */ && isSpecialTemplateDirective(p.name))) {
1123
+ tagType = 3 /* TEMPLATE */;
1124
+ }
1125
+ }
1126
+ else if (isComponent(tag, props, context)) {
1127
+ tagType = 1 /* COMPONENT */;
1081
1128
  }
1082
1129
  }
1083
1130
  return {
@@ -1092,6 +1139,49 @@ function parseTag(context, type, parent) {
1092
1139
  codegenNode: undefined // to be created during transform phase
1093
1140
  };
1094
1141
  }
1142
+ function isComponent(tag, props, context) {
1143
+ const options = context.options;
1144
+ if (options.isCustomElement(tag)) {
1145
+ return false;
1146
+ }
1147
+ if (tag === 'component' ||
1148
+ /^[A-Z]/.test(tag) ||
1149
+ isCoreComponent(tag) ||
1150
+ (options.isBuiltInComponent && options.isBuiltInComponent(tag)) ||
1151
+ (options.isNativeTag && !options.isNativeTag(tag))) {
1152
+ return true;
1153
+ }
1154
+ // at this point the tag should be a native tag, but check for potential "is"
1155
+ // casting
1156
+ for (let i = 0; i < props.length; i++) {
1157
+ const p = props[i];
1158
+ if (p.type === 6 /* ATTRIBUTE */) {
1159
+ if (p.name === 'is' && p.value) {
1160
+ if (p.value.content.startsWith('vue:')) {
1161
+ return true;
1162
+ }
1163
+ else if (checkCompatEnabled("COMPILER_IS_ON_ELEMENT" /* COMPILER_IS_ON_ELEMENT */, context, p.loc)) {
1164
+ return true;
1165
+ }
1166
+ }
1167
+ }
1168
+ else {
1169
+ // directive
1170
+ // v-is (TODO Deprecate)
1171
+ if (p.name === 'is') {
1172
+ return true;
1173
+ }
1174
+ else if (
1175
+ // :is on plain element - only treat as component in compat mode
1176
+ p.name === 'bind' &&
1177
+ isBindKey(p.arg, 'is') &&
1178
+ true &&
1179
+ checkCompatEnabled("COMPILER_IS_ON_ELEMENT" /* COMPILER_IS_ON_ELEMENT */, context, p.loc)) {
1180
+ return true;
1181
+ }
1182
+ }
1183
+ }
1184
+ }
1095
1185
  function parseAttributes(context, type) {
1096
1186
  const props = [];
1097
1187
  const attributeNames = new Set();
@@ -1988,7 +2078,7 @@ function createStructuralDirectiveTransform(name, fn) {
1988
2078
 
1989
2079
  const PURE_ANNOTATION = `/*#__PURE__*/`;
1990
2080
  const WITH_ID = `_withId`;
1991
- function createCodegenContext(ast, { mode = 'function', prefixIdentifiers = mode === 'module', sourceMap: sourceMap$1 = false, filename = `template.vue.html`, scopeId = null, optimizeImports = false, runtimeGlobalName = `Vue`, runtimeModuleName = `vue`, ssr = false }) {
2081
+ function createCodegenContext(ast, { mode = 'function', prefixIdentifiers = mode === 'module', sourceMap: sourceMap$1 = false, filename = `template.vue.html`, scopeId = null, optimizeImports = false, runtimeGlobalName = `Vue`, runtimeModuleName = `vue`, ssr = false, isTS = false }) {
1992
2082
  const context = {
1993
2083
  mode,
1994
2084
  prefixIdentifiers,
@@ -1999,6 +2089,7 @@ function createCodegenContext(ast, { mode = 'function', prefixIdentifiers = mode
1999
2089
  runtimeGlobalName,
2000
2090
  runtimeModuleName,
2001
2091
  ssr,
2092
+ isTS,
2002
2093
  source: ast.loc.source,
2003
2094
  code: ``,
2004
2095
  column: 1,
@@ -2278,7 +2369,7 @@ function genModulePreamble(ast, context, genScopeId, inline) {
2278
2369
  push(`export `);
2279
2370
  }
2280
2371
  }
2281
- function genAssets(assets, type, { helper, push, newline }) {
2372
+ function genAssets(assets, type, { helper, push, newline, isTS }) {
2282
2373
  const resolver = helper(type === 'filter'
2283
2374
  ? RESOLVE_FILTER
2284
2375
  : type === 'component'
@@ -2291,7 +2382,7 @@ function genAssets(assets, type, { helper, push, newline }) {
2291
2382
  if (maybeSelfReference) {
2292
2383
  id = id.slice(0, -6);
2293
2384
  }
2294
- push(`const ${toValidAssetId(id, type)} = ${resolver}(${JSON.stringify(id)}${maybeSelfReference ? `, true` : ``})`);
2385
+ push(`const ${toValidAssetId(id, type)} = ${resolver}(${JSON.stringify(id)}${maybeSelfReference ? `, true` : ``})${isTS ? `!` : ``}`);
2295
2386
  if (i < assets.length - 1) {
2296
2387
  newline();
2297
2388
  }
@@ -3804,7 +3895,8 @@ function hasForwardedSlots(children) {
3804
3895
  switch (child.type) {
3805
3896
  case 1 /* ELEMENT */:
3806
3897
  if (child.tagType === 2 /* SLOT */ ||
3807
- (child.tagType === 0 /* ELEMENT */ &&
3898
+ ((child.tagType === 0 /* ELEMENT */ ||
3899
+ child.tagType === 3 /* TEMPLATE */) &&
3808
3900
  hasForwardedSlots(child.children))) {
3809
3901
  return true;
3810
3902
  }
@@ -3968,16 +4060,10 @@ function resolveComponentType(node, context, ssr = false) {
3968
4060
  let { tag } = node;
3969
4061
  // 1. dynamic component
3970
4062
  const isExplicitDynamic = isComponentTag(tag);
3971
- const isProp = findProp(node, 'is') || (!isExplicitDynamic && findDir(node, 'is'));
4063
+ const isProp = findProp(node, 'is');
3972
4064
  if (isProp) {
3973
- if (!isExplicitDynamic && isProp.type === 6 /* ATTRIBUTE */) {
3974
- // <button is="vue:xxx">
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 {
4065
+ if (isExplicitDynamic ||
4066
+ (isCompatEnabled("COMPILER_IS_ON_ELEMENT" /* COMPILER_IS_ON_ELEMENT */, context))) {
3981
4067
  const exp = isProp.type === 6 /* ATTRIBUTE */
3982
4068
  ? isProp.value && createSimpleExpression(isProp.value.content, true)
3983
4069
  : isProp.exp;
@@ -3987,6 +4073,21 @@ function resolveComponentType(node, context, ssr = false) {
3987
4073
  ]);
3988
4074
  }
3989
4075
  }
4076
+ else if (isProp.type === 6 /* ATTRIBUTE */ &&
4077
+ isProp.value.content.startsWith('vue:')) {
4078
+ // <button is="vue:xxx">
4079
+ // if not <component>, only is value that starts with "vue:" will be
4080
+ // treated as component by the parse phase and reach here, unless it's
4081
+ // compat mode where all is values are considered components
4082
+ tag = isProp.value.content.slice(4);
4083
+ }
4084
+ }
4085
+ // 1.5 v-is (TODO: Deprecate)
4086
+ const isDir = !isExplicitDynamic && findDir(node, 'is');
4087
+ if (isDir && isDir.exp) {
4088
+ return createCallExpression(context.helper(RESOLVE_DYNAMIC_COMPONENT), [
4089
+ isDir.exp
4090
+ ]);
3990
4091
  }
3991
4092
  // 2. built-in components (Teleport, Transition, KeepAlive, Suspense...)
3992
4093
  const builtIn = isCoreComponent(tag) || context.isBuiltInComponent(tag);
@@ -4130,7 +4231,9 @@ function buildProps(node, context, props = node.props, ssr = false) {
4130
4231
  }
4131
4232
  // skip is on <component>, or is="vue:xxx"
4132
4233
  if (name === 'is' &&
4133
- (isComponentTag(tag) || (value && value.content.startsWith('vue:')))) {
4234
+ (isComponentTag(tag) ||
4235
+ (value && value.content.startsWith('vue:')) ||
4236
+ (isCompatEnabled("COMPILER_IS_ON_ELEMENT" /* COMPILER_IS_ON_ELEMENT */, context)))) {
4134
4237
  continue;
4135
4238
  }
4136
4239
  properties.push(createObjectProperty(createSimpleExpression(name, true, getInnerRange(loc, 0, name.length)), createSimpleExpression(value ? value.content : '', isStatic, value ? value.loc : loc)));
@@ -4153,7 +4256,10 @@ function buildProps(node, context, props = node.props, ssr = false) {
4153
4256
  }
4154
4257
  // skip v-is and :is on <component>
4155
4258
  if (name === 'is' ||
4156
- (isVBind && isComponentTag(tag) && isBindKey(arg, 'is'))) {
4259
+ (isVBind &&
4260
+ isBindKey(arg, 'is') &&
4261
+ (isComponentTag(tag) ||
4262
+ (isCompatEnabled("COMPILER_IS_ON_ELEMENT" /* COMPILER_IS_ON_ELEMENT */, context))))) {
4157
4263
  continue;
4158
4264
  }
4159
4265
  // skip v-on in SSR compilation
@@ -4337,7 +4443,7 @@ function buildDirectiveArgs(dir, context) {
4337
4443
  else {
4338
4444
  // user directive.
4339
4445
  // see if we have directives exposed via <script setup>
4340
- const fromSetup = resolveSetupReference(dir.name, context);
4446
+ const fromSetup = resolveSetupReference('v-' + dir.name, context);
4341
4447
  if (fromSetup) {
4342
4448
  dirArgs.push(fromSetup);
4343
4449
  }
@@ -4737,7 +4843,7 @@ const transformModel = (dir, node, context) => {
4737
4843
  const maybeRef = context.inline &&
4738
4844
  bindingType &&
4739
4845
  bindingType !== "setup-const" /* SETUP_CONST */;
4740
- if (!isMemberExpression(expString) && !maybeRef) {
4846
+ if (!expString.trim() || (!isMemberExpression(expString) && !maybeRef)) {
4741
4847
  context.onError(createCompilerError(41 /* X_V_MODEL_MALFORMED_EXPRESSION */, exp.loc));
4742
4848
  return createTransformProps();
4743
4849
  }
@@ -355,18 +355,84 @@ function isCoreComponent(tag) {
355
355
  }
356
356
  const nonIdentifierRE = /^\d|[^\$\w]/;
357
357
  const isSimpleIdentifier = (name) => !nonIdentifierRE.test(name);
358
- const memberExpRE = /^[A-Za-z_$\xA0-\uFFFF][\w$\xA0-\uFFFF]*(?:\s*\.\s*[A-Za-z_$\xA0-\uFFFF][\w$\xA0-\uFFFF]*|\[(.+)\])*$/;
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
- if (!path)
361
- return false;
362
- const matched = memberExpRE.exec(path.trim());
363
- if (!matched)
364
- return false;
365
- if (!matched[1])
366
- return true;
367
- if (!/[\[\]]/.test(matched[1]))
368
- return true;
369
- return isMemberExpression(matched[1].trim());
368
+ // remove whitespaces around . or [ first
369
+ path = path.trim().replace(whitespaceRE, s => s.trim());
370
+ let state = 0 /* inMemberExp */;
371
+ let stateStack = [];
372
+ let currentOpenBracketCount = 0;
373
+ let currentOpenParensCount = 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
+ stateStack.push(state);
381
+ state = 1 /* inBrackets */;
382
+ currentOpenBracketCount++;
383
+ }
384
+ else if (char === '(') {
385
+ stateStack.push(state);
386
+ state = 2 /* inParens */;
387
+ currentOpenParensCount++;
388
+ }
389
+ else if (!(i === 0 ? validFirstIdentCharRE : validIdentCharRE).test(char)) {
390
+ return false;
391
+ }
392
+ break;
393
+ case 1 /* inBrackets */:
394
+ if (char === `'` || char === `"` || char === '`') {
395
+ stateStack.push(state);
396
+ state = 3 /* inString */;
397
+ currentStringType = char;
398
+ }
399
+ else if (char === `[`) {
400
+ currentOpenBracketCount++;
401
+ }
402
+ else if (char === `]`) {
403
+ if (!--currentOpenBracketCount) {
404
+ state = stateStack.pop();
405
+ }
406
+ }
407
+ break;
408
+ case 2 /* inParens */:
409
+ if (char === `'` || char === `"` || char === '`') {
410
+ stateStack.push(state);
411
+ state = 3 /* inString */;
412
+ currentStringType = char;
413
+ }
414
+ else if (char === `(`) {
415
+ currentOpenParensCount++;
416
+ }
417
+ else if (char === `)`) {
418
+ // if the exp ends as a call then it should not be considered valid
419
+ if (i === path.length - 1) {
420
+ return false;
421
+ }
422
+ if (!--currentOpenParensCount) {
423
+ state = stateStack.pop();
424
+ }
425
+ }
426
+ break;
427
+ case 3 /* inString */:
428
+ if (char === currentStringType) {
429
+ state = stateStack.pop();
430
+ currentStringType = null;
431
+ }
432
+ break;
433
+ }
434
+ }
435
+ return !currentOpenBracketCount && !currentOpenParensCount;
370
436
  };
371
437
  function getInnerRange(loc, offset, length) {
372
438
  const source = loc.source.substr(offset, length);
@@ -937,6 +1003,10 @@ function parseElement(context, ancestors) {
937
1003
  const isPreBoundary = context.inPre && !wasInPre;
938
1004
  const isVPreBoundary = context.inVPre && !wasInVPre;
939
1005
  if (element.isSelfClosing || context.options.isVoidTag(element.tag)) {
1006
+ // #4030 self-closing <pre> tag
1007
+ if (context.options.isPreTag(element.tag)) {
1008
+ context.inPre = false;
1009
+ }
940
1010
  return element;
941
1011
  }
942
1012
  // Children.
@@ -992,12 +1062,13 @@ function parseTag(context, type, parent) {
992
1062
  // save current state in case we need to re-parse attributes with v-pre
993
1063
  const cursor = getCursor(context);
994
1064
  const currentSource = context.source;
995
- // Attributes.
996
- let props = parseAttributes(context, type);
997
1065
  // check <pre> tag
998
- if (context.options.isPreTag(tag)) {
1066
+ const isPreTag = context.options.isPreTag(tag);
1067
+ if (isPreTag) {
999
1068
  context.inPre = true;
1000
1069
  }
1070
+ // Attributes.
1071
+ let props = parseAttributes(context, type);
1001
1072
  // check v-pre
1002
1073
  if (type === 0 /* Start */ &&
1003
1074
  !context.inVPre &&
@@ -1025,41 +1096,17 @@ function parseTag(context, type, parent) {
1025
1096
  return;
1026
1097
  }
1027
1098
  let tagType = 0 /* ELEMENT */;
1028
- const options = context.options;
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
- }
1099
+ if (!context.inVPre) {
1057
1100
  if (tag === 'slot') {
1058
1101
  tagType = 2 /* SLOT */;
1059
1102
  }
1060
- else if (tag === 'template' &&
1061
- props.some(p => p.type === 7 /* DIRECTIVE */ && isSpecialTemplateDirective(p.name))) {
1062
- tagType = 3 /* TEMPLATE */;
1103
+ else if (tag === 'template') {
1104
+ if (props.some(p => p.type === 7 /* DIRECTIVE */ && isSpecialTemplateDirective(p.name))) {
1105
+ tagType = 3 /* TEMPLATE */;
1106
+ }
1107
+ }
1108
+ else if (isComponent(tag, props, context)) {
1109
+ tagType = 1 /* COMPONENT */;
1063
1110
  }
1064
1111
  }
1065
1112
  return {
@@ -1074,6 +1121,49 @@ function parseTag(context, type, parent) {
1074
1121
  codegenNode: undefined // to be created during transform phase
1075
1122
  };
1076
1123
  }
1124
+ function isComponent(tag, props, context) {
1125
+ const options = context.options;
1126
+ if (options.isCustomElement(tag)) {
1127
+ return false;
1128
+ }
1129
+ if (tag === 'component' ||
1130
+ /^[A-Z]/.test(tag) ||
1131
+ isCoreComponent(tag) ||
1132
+ (options.isBuiltInComponent && options.isBuiltInComponent(tag)) ||
1133
+ (options.isNativeTag && !options.isNativeTag(tag))) {
1134
+ return true;
1135
+ }
1136
+ // at this point the tag should be a native tag, but check for potential "is"
1137
+ // casting
1138
+ for (let i = 0; i < props.length; i++) {
1139
+ const p = props[i];
1140
+ if (p.type === 6 /* ATTRIBUTE */) {
1141
+ if (p.name === 'is' && p.value) {
1142
+ if (p.value.content.startsWith('vue:')) {
1143
+ return true;
1144
+ }
1145
+ else if (checkCompatEnabled("COMPILER_IS_ON_ELEMENT" /* COMPILER_IS_ON_ELEMENT */, context, p.loc)) {
1146
+ return true;
1147
+ }
1148
+ }
1149
+ }
1150
+ else {
1151
+ // directive
1152
+ // v-is (TODO Deprecate)
1153
+ if (p.name === 'is') {
1154
+ return true;
1155
+ }
1156
+ else if (
1157
+ // :is on plain element - only treat as component in compat mode
1158
+ p.name === 'bind' &&
1159
+ isBindKey(p.arg, 'is') &&
1160
+ true &&
1161
+ checkCompatEnabled("COMPILER_IS_ON_ELEMENT" /* COMPILER_IS_ON_ELEMENT */, context, p.loc)) {
1162
+ return true;
1163
+ }
1164
+ }
1165
+ }
1166
+ }
1077
1167
  function parseAttributes(context, type) {
1078
1168
  const props = [];
1079
1169
  const attributeNames = new Set();
@@ -1945,7 +2035,7 @@ function createStructuralDirectiveTransform(name, fn) {
1945
2035
 
1946
2036
  const PURE_ANNOTATION = `/*#__PURE__*/`;
1947
2037
  const WITH_ID = `_withId`;
1948
- function createCodegenContext(ast, { mode = 'function', prefixIdentifiers = mode === 'module', sourceMap: sourceMap$1 = false, filename = `template.vue.html`, scopeId = null, optimizeImports = false, runtimeGlobalName = `Vue`, runtimeModuleName = `vue`, ssr = false }) {
2038
+ function createCodegenContext(ast, { mode = 'function', prefixIdentifiers = mode === 'module', sourceMap: sourceMap$1 = false, filename = `template.vue.html`, scopeId = null, optimizeImports = false, runtimeGlobalName = `Vue`, runtimeModuleName = `vue`, ssr = false, isTS = false }) {
1949
2039
  const context = {
1950
2040
  mode,
1951
2041
  prefixIdentifiers,
@@ -1956,6 +2046,7 @@ function createCodegenContext(ast, { mode = 'function', prefixIdentifiers = mode
1956
2046
  runtimeGlobalName,
1957
2047
  runtimeModuleName,
1958
2048
  ssr,
2049
+ isTS,
1959
2050
  source: ast.loc.source,
1960
2051
  code: ``,
1961
2052
  column: 1,
@@ -2235,7 +2326,7 @@ function genModulePreamble(ast, context, genScopeId, inline) {
2235
2326
  push(`export `);
2236
2327
  }
2237
2328
  }
2238
- function genAssets(assets, type, { helper, push, newline }) {
2329
+ function genAssets(assets, type, { helper, push, newline, isTS }) {
2239
2330
  const resolver = helper(type === 'filter'
2240
2331
  ? RESOLVE_FILTER
2241
2332
  : type === 'component'
@@ -2248,7 +2339,7 @@ function genAssets(assets, type, { helper, push, newline }) {
2248
2339
  if (maybeSelfReference) {
2249
2340
  id = id.slice(0, -6);
2250
2341
  }
2251
- push(`const ${toValidAssetId(id, type)} = ${resolver}(${JSON.stringify(id)}${maybeSelfReference ? `, true` : ``})`);
2342
+ push(`const ${toValidAssetId(id, type)} = ${resolver}(${JSON.stringify(id)}${maybeSelfReference ? `, true` : ``})${isTS ? `!` : ``}`);
2252
2343
  if (i < assets.length - 1) {
2253
2344
  newline();
2254
2345
  }
@@ -3728,7 +3819,8 @@ function hasForwardedSlots(children) {
3728
3819
  switch (child.type) {
3729
3820
  case 1 /* ELEMENT */:
3730
3821
  if (child.tagType === 2 /* SLOT */ ||
3731
- (child.tagType === 0 /* ELEMENT */ &&
3822
+ ((child.tagType === 0 /* ELEMENT */ ||
3823
+ child.tagType === 3 /* TEMPLATE */) &&
3732
3824
  hasForwardedSlots(child.children))) {
3733
3825
  return true;
3734
3826
  }
@@ -3873,16 +3965,10 @@ function resolveComponentType(node, context, ssr = false) {
3873
3965
  let { tag } = node;
3874
3966
  // 1. dynamic component
3875
3967
  const isExplicitDynamic = isComponentTag(tag);
3876
- const isProp = findProp(node, 'is') || (!isExplicitDynamic && findDir(node, 'is'));
3968
+ const isProp = findProp(node, 'is');
3877
3969
  if (isProp) {
3878
- if (!isExplicitDynamic && isProp.type === 6 /* ATTRIBUTE */) {
3879
- // <button is="vue:xxx">
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 {
3970
+ if (isExplicitDynamic ||
3971
+ (isCompatEnabled("COMPILER_IS_ON_ELEMENT" /* COMPILER_IS_ON_ELEMENT */, context))) {
3886
3972
  const exp = isProp.type === 6 /* ATTRIBUTE */
3887
3973
  ? isProp.value && createSimpleExpression(isProp.value.content, true)
3888
3974
  : isProp.exp;
@@ -3892,6 +3978,21 @@ function resolveComponentType(node, context, ssr = false) {
3892
3978
  ]);
3893
3979
  }
3894
3980
  }
3981
+ else if (isProp.type === 6 /* ATTRIBUTE */ &&
3982
+ isProp.value.content.startsWith('vue:')) {
3983
+ // <button is="vue:xxx">
3984
+ // if not <component>, only is value that starts with "vue:" will be
3985
+ // treated as component by the parse phase and reach here, unless it's
3986
+ // compat mode where all is values are considered components
3987
+ tag = isProp.value.content.slice(4);
3988
+ }
3989
+ }
3990
+ // 1.5 v-is (TODO: Deprecate)
3991
+ const isDir = !isExplicitDynamic && findDir(node, 'is');
3992
+ if (isDir && isDir.exp) {
3993
+ return createCallExpression(context.helper(RESOLVE_DYNAMIC_COMPONENT), [
3994
+ isDir.exp
3995
+ ]);
3895
3996
  }
3896
3997
  // 2. built-in components (Teleport, Transition, KeepAlive, Suspense...)
3897
3998
  const builtIn = isCoreComponent(tag) || context.isBuiltInComponent(tag);
@@ -4035,7 +4136,9 @@ function buildProps(node, context, props = node.props, ssr = false) {
4035
4136
  }
4036
4137
  // skip is on <component>, or is="vue:xxx"
4037
4138
  if (name === 'is' &&
4038
- (isComponentTag(tag) || (value && value.content.startsWith('vue:')))) {
4139
+ (isComponentTag(tag) ||
4140
+ (value && value.content.startsWith('vue:')) ||
4141
+ (isCompatEnabled("COMPILER_IS_ON_ELEMENT" /* COMPILER_IS_ON_ELEMENT */, context)))) {
4039
4142
  continue;
4040
4143
  }
4041
4144
  properties.push(createObjectProperty(createSimpleExpression(name, true, getInnerRange(loc, 0, name.length)), createSimpleExpression(value ? value.content : '', isStatic, value ? value.loc : loc)));
@@ -4058,7 +4161,10 @@ function buildProps(node, context, props = node.props, ssr = false) {
4058
4161
  }
4059
4162
  // skip v-is and :is on <component>
4060
4163
  if (name === 'is' ||
4061
- (isVBind && isComponentTag(tag) && isBindKey(arg, 'is'))) {
4164
+ (isVBind &&
4165
+ isBindKey(arg, 'is') &&
4166
+ (isComponentTag(tag) ||
4167
+ (isCompatEnabled("COMPILER_IS_ON_ELEMENT" /* COMPILER_IS_ON_ELEMENT */, context))))) {
4062
4168
  continue;
4063
4169
  }
4064
4170
  // skip v-on in SSR compilation
@@ -4219,7 +4325,7 @@ function buildDirectiveArgs(dir, context) {
4219
4325
  else {
4220
4326
  // user directive.
4221
4327
  // see if we have directives exposed via <script setup>
4222
- const fromSetup = resolveSetupReference(dir.name, context);
4328
+ const fromSetup = resolveSetupReference('v-' + dir.name, context);
4223
4329
  if (fromSetup) {
4224
4330
  dirArgs.push(fromSetup);
4225
4331
  }
@@ -4616,7 +4722,7 @@ const transformModel = (dir, node, context) => {
4616
4722
  const maybeRef = context.inline &&
4617
4723
  bindingType &&
4618
4724
  bindingType !== "setup-const" /* SETUP_CONST */;
4619
- if (!isMemberExpression(expString) && !maybeRef) {
4725
+ if (!expString.trim() || (!isMemberExpression(expString) && !maybeRef)) {
4620
4726
  context.onError(createCompilerError(41 /* X_V_MODEL_MALFORMED_EXPRESSION */, exp.loc));
4621
4727
  return createTransformProps();
4622
4728
  }
@@ -119,7 +119,7 @@ export declare const CAPITALIZE: unique symbol;
119
119
 
120
120
  export declare function checkCompatEnabled(key: CompilerDeprecationTypes, context: ParserContext | TransformContext, loc: SourceLocation | null, ...args: any[]): boolean;
121
121
 
122
- export declare interface CodegenContext extends Omit<Required<CodegenOptions>, 'bindingMetadata' | 'inline' | 'isTS'> {
122
+ export declare interface CodegenContext extends Omit<Required<CodegenOptions>, 'bindingMetadata' | 'inline'> {
123
123
  source: string;
124
124
  code: string;
125
125
  line: number;
@@ -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,84 @@ function isCoreComponent(tag) {
351
351
  }
352
352
  const nonIdentifierRE = /^\d|[^\$\w]/;
353
353
  const isSimpleIdentifier = (name) => !nonIdentifierRE.test(name);
354
- const memberExpRE = /^[A-Za-z_$\xA0-\uFFFF][\w$\xA0-\uFFFF]*(?:\s*\.\s*[A-Za-z_$\xA0-\uFFFF][\w$\xA0-\uFFFF]*|\[(.+)\])*$/;
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
- if (!path)
357
- return false;
358
- const matched = memberExpRE.exec(path.trim());
359
- if (!matched)
360
- return false;
361
- if (!matched[1])
362
- return true;
363
- if (!/[\[\]]/.test(matched[1]))
364
- return true;
365
- return isMemberExpression(matched[1].trim());
364
+ // remove whitespaces around . or [ first
365
+ path = path.trim().replace(whitespaceRE, s => s.trim());
366
+ let state = 0 /* inMemberExp */;
367
+ let stateStack = [];
368
+ let currentOpenBracketCount = 0;
369
+ let currentOpenParensCount = 0;
370
+ let currentStringType = null;
371
+ for (let i = 0; i < path.length; i++) {
372
+ const char = path.charAt(i);
373
+ switch (state) {
374
+ case 0 /* inMemberExp */:
375
+ if (char === '[') {
376
+ stateStack.push(state);
377
+ state = 1 /* inBrackets */;
378
+ currentOpenBracketCount++;
379
+ }
380
+ else if (char === '(') {
381
+ stateStack.push(state);
382
+ state = 2 /* inParens */;
383
+ currentOpenParensCount++;
384
+ }
385
+ else if (!(i === 0 ? validFirstIdentCharRE : validIdentCharRE).test(char)) {
386
+ return false;
387
+ }
388
+ break;
389
+ case 1 /* inBrackets */:
390
+ if (char === `'` || char === `"` || char === '`') {
391
+ stateStack.push(state);
392
+ state = 3 /* inString */;
393
+ currentStringType = char;
394
+ }
395
+ else if (char === `[`) {
396
+ currentOpenBracketCount++;
397
+ }
398
+ else if (char === `]`) {
399
+ if (!--currentOpenBracketCount) {
400
+ state = stateStack.pop();
401
+ }
402
+ }
403
+ break;
404
+ case 2 /* inParens */:
405
+ if (char === `'` || char === `"` || char === '`') {
406
+ stateStack.push(state);
407
+ state = 3 /* inString */;
408
+ currentStringType = char;
409
+ }
410
+ else if (char === `(`) {
411
+ currentOpenParensCount++;
412
+ }
413
+ else if (char === `)`) {
414
+ // if the exp ends as a call then it should not be considered valid
415
+ if (i === path.length - 1) {
416
+ return false;
417
+ }
418
+ if (!--currentOpenParensCount) {
419
+ state = stateStack.pop();
420
+ }
421
+ }
422
+ break;
423
+ case 3 /* inString */:
424
+ if (char === currentStringType) {
425
+ state = stateStack.pop();
426
+ currentStringType = null;
427
+ }
428
+ break;
429
+ }
430
+ }
431
+ return !currentOpenBracketCount && !currentOpenParensCount;
366
432
  };
367
433
  function getInnerRange(loc, offset, length) {
368
434
  const source = loc.source.substr(offset, length);
@@ -940,6 +1006,10 @@ function parseElement(context, ancestors) {
940
1006
  const isPreBoundary = context.inPre && !wasInPre;
941
1007
  const isVPreBoundary = context.inVPre && !wasInVPre;
942
1008
  if (element.isSelfClosing || context.options.isVoidTag(element.tag)) {
1009
+ // #4030 self-closing <pre> tag
1010
+ if (context.options.isPreTag(element.tag)) {
1011
+ context.inPre = false;
1012
+ }
943
1013
  return element;
944
1014
  }
945
1015
  // Children.
@@ -995,12 +1065,13 @@ function parseTag(context, type, parent) {
995
1065
  // save current state in case we need to re-parse attributes with v-pre
996
1066
  const cursor = getCursor(context);
997
1067
  const currentSource = context.source;
998
- // Attributes.
999
- let props = parseAttributes(context, type);
1000
1068
  // check <pre> tag
1001
- if (context.options.isPreTag(tag)) {
1069
+ const isPreTag = context.options.isPreTag(tag);
1070
+ if (isPreTag) {
1002
1071
  context.inPre = true;
1003
1072
  }
1073
+ // Attributes.
1074
+ let props = parseAttributes(context, type);
1004
1075
  // check v-pre
1005
1076
  if (type === 0 /* Start */ &&
1006
1077
  !context.inVPre &&
@@ -1048,41 +1119,17 @@ function parseTag(context, type, parent) {
1048
1119
  }
1049
1120
  }
1050
1121
  let tagType = 0 /* ELEMENT */;
1051
- const options = context.options;
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
- }
1122
+ if (!context.inVPre) {
1080
1123
  if (tag === 'slot') {
1081
1124
  tagType = 2 /* SLOT */;
1082
1125
  }
1083
- else if (tag === 'template' &&
1084
- props.some(p => p.type === 7 /* DIRECTIVE */ && isSpecialTemplateDirective(p.name))) {
1085
- tagType = 3 /* TEMPLATE */;
1126
+ else if (tag === 'template') {
1127
+ if (props.some(p => p.type === 7 /* DIRECTIVE */ && isSpecialTemplateDirective(p.name))) {
1128
+ tagType = 3 /* TEMPLATE */;
1129
+ }
1130
+ }
1131
+ else if (isComponent(tag, props, context)) {
1132
+ tagType = 1 /* COMPONENT */;
1086
1133
  }
1087
1134
  }
1088
1135
  return {
@@ -1097,6 +1144,49 @@ function parseTag(context, type, parent) {
1097
1144
  codegenNode: undefined // to be created during transform phase
1098
1145
  };
1099
1146
  }
1147
+ function isComponent(tag, props, context) {
1148
+ const options = context.options;
1149
+ if (options.isCustomElement(tag)) {
1150
+ return false;
1151
+ }
1152
+ if (tag === 'component' ||
1153
+ /^[A-Z]/.test(tag) ||
1154
+ isCoreComponent(tag) ||
1155
+ (options.isBuiltInComponent && options.isBuiltInComponent(tag)) ||
1156
+ (options.isNativeTag && !options.isNativeTag(tag))) {
1157
+ return true;
1158
+ }
1159
+ // at this point the tag should be a native tag, but check for potential "is"
1160
+ // casting
1161
+ for (let i = 0; i < props.length; i++) {
1162
+ const p = props[i];
1163
+ if (p.type === 6 /* ATTRIBUTE */) {
1164
+ if (p.name === 'is' && p.value) {
1165
+ if (p.value.content.startsWith('vue:')) {
1166
+ return true;
1167
+ }
1168
+ else if (checkCompatEnabled("COMPILER_IS_ON_ELEMENT" /* COMPILER_IS_ON_ELEMENT */, context, p.loc)) {
1169
+ return true;
1170
+ }
1171
+ }
1172
+ }
1173
+ else {
1174
+ // directive
1175
+ // v-is (TODO Deprecate)
1176
+ if (p.name === 'is') {
1177
+ return true;
1178
+ }
1179
+ else if (
1180
+ // :is on plain element - only treat as component in compat mode
1181
+ p.name === 'bind' &&
1182
+ isBindKey(p.arg, 'is') &&
1183
+ true &&
1184
+ checkCompatEnabled("COMPILER_IS_ON_ELEMENT" /* COMPILER_IS_ON_ELEMENT */, context, p.loc)) {
1185
+ return true;
1186
+ }
1187
+ }
1188
+ }
1189
+ }
1100
1190
  function parseAttributes(context, type) {
1101
1191
  const props = [];
1102
1192
  const attributeNames = new Set();
@@ -1961,7 +2051,7 @@ function createStructuralDirectiveTransform(name, fn) {
1961
2051
  }
1962
2052
 
1963
2053
  const PURE_ANNOTATION = `/*#__PURE__*/`;
1964
- function createCodegenContext(ast, { mode = 'function', prefixIdentifiers = mode === 'module', sourceMap = false, filename = `template.vue.html`, scopeId = null, optimizeImports = false, runtimeGlobalName = `Vue`, runtimeModuleName = `vue`, ssr = false }) {
2054
+ function createCodegenContext(ast, { mode = 'function', prefixIdentifiers = mode === 'module', sourceMap = false, filename = `template.vue.html`, scopeId = null, optimizeImports = false, runtimeGlobalName = `Vue`, runtimeModuleName = `vue`, ssr = false, isTS = false }) {
1965
2055
  const context = {
1966
2056
  mode,
1967
2057
  prefixIdentifiers,
@@ -1972,6 +2062,7 @@ function createCodegenContext(ast, { mode = 'function', prefixIdentifiers = mode
1972
2062
  runtimeGlobalName,
1973
2063
  runtimeModuleName,
1974
2064
  ssr,
2065
+ isTS,
1975
2066
  source: ast.loc.source,
1976
2067
  code: ``,
1977
2068
  column: 1,
@@ -2127,7 +2218,7 @@ function genFunctionPreamble(ast, context) {
2127
2218
  newline();
2128
2219
  push(`return `);
2129
2220
  }
2130
- function genAssets(assets, type, { helper, push, newline }) {
2221
+ function genAssets(assets, type, { helper, push, newline, isTS }) {
2131
2222
  const resolver = helper(type === 'filter'
2132
2223
  ? RESOLVE_FILTER
2133
2224
  : type === 'component'
@@ -2140,7 +2231,7 @@ function genAssets(assets, type, { helper, push, newline }) {
2140
2231
  if (maybeSelfReference) {
2141
2232
  id = id.slice(0, -6);
2142
2233
  }
2143
- push(`const ${toValidAssetId(id, type)} = ${resolver}(${JSON.stringify(id)}${maybeSelfReference ? `, true` : ``})`);
2234
+ push(`const ${toValidAssetId(id, type)} = ${resolver}(${JSON.stringify(id)}${maybeSelfReference ? `, true` : ``})${isTS ? `!` : ``}`);
2144
2235
  if (i < assets.length - 1) {
2145
2236
  newline();
2146
2237
  }
@@ -3253,7 +3344,8 @@ function hasForwardedSlots(children) {
3253
3344
  switch (child.type) {
3254
3345
  case 1 /* ELEMENT */:
3255
3346
  if (child.tagType === 2 /* SLOT */ ||
3256
- (child.tagType === 0 /* ELEMENT */ &&
3347
+ ((child.tagType === 0 /* ELEMENT */ ||
3348
+ child.tagType === 3 /* TEMPLATE */) &&
3257
3349
  hasForwardedSlots(child.children))) {
3258
3350
  return true;
3259
3351
  }
@@ -3420,16 +3512,10 @@ function resolveComponentType(node, context, ssr = false) {
3420
3512
  let { tag } = node;
3421
3513
  // 1. dynamic component
3422
3514
  const isExplicitDynamic = isComponentTag(tag);
3423
- const isProp = findProp(node, 'is') || (!isExplicitDynamic && findDir(node, 'is'));
3515
+ const isProp = findProp(node, 'is');
3424
3516
  if (isProp) {
3425
- if (!isExplicitDynamic && isProp.type === 6 /* ATTRIBUTE */) {
3426
- // <button is="vue:xxx">
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 {
3517
+ if (isExplicitDynamic ||
3518
+ (isCompatEnabled("COMPILER_IS_ON_ELEMENT" /* COMPILER_IS_ON_ELEMENT */, context))) {
3433
3519
  const exp = isProp.type === 6 /* ATTRIBUTE */
3434
3520
  ? isProp.value && createSimpleExpression(isProp.value.content, true)
3435
3521
  : isProp.exp;
@@ -3439,6 +3525,21 @@ function resolveComponentType(node, context, ssr = false) {
3439
3525
  ]);
3440
3526
  }
3441
3527
  }
3528
+ else if (isProp.type === 6 /* ATTRIBUTE */ &&
3529
+ isProp.value.content.startsWith('vue:')) {
3530
+ // <button is="vue:xxx">
3531
+ // if not <component>, only is value that starts with "vue:" will be
3532
+ // treated as component by the parse phase and reach here, unless it's
3533
+ // compat mode where all is values are considered components
3534
+ tag = isProp.value.content.slice(4);
3535
+ }
3536
+ }
3537
+ // 1.5 v-is (TODO: Deprecate)
3538
+ const isDir = !isExplicitDynamic && findDir(node, 'is');
3539
+ if (isDir && isDir.exp) {
3540
+ return createCallExpression(context.helper(RESOLVE_DYNAMIC_COMPONENT), [
3541
+ isDir.exp
3542
+ ]);
3442
3543
  }
3443
3544
  // 2. built-in components (Teleport, Transition, KeepAlive, Suspense...)
3444
3545
  const builtIn = isCoreComponent(tag) || context.isBuiltInComponent(tag);
@@ -3522,7 +3623,9 @@ function buildProps(node, context, props = node.props, ssr = false) {
3522
3623
  }
3523
3624
  // skip is on <component>, or is="vue:xxx"
3524
3625
  if (name === 'is' &&
3525
- (isComponentTag(tag) || (value && value.content.startsWith('vue:')))) {
3626
+ (isComponentTag(tag) ||
3627
+ (value && value.content.startsWith('vue:')) ||
3628
+ (isCompatEnabled("COMPILER_IS_ON_ELEMENT" /* COMPILER_IS_ON_ELEMENT */, context)))) {
3526
3629
  continue;
3527
3630
  }
3528
3631
  properties.push(createObjectProperty(createSimpleExpression(name, true, getInnerRange(loc, 0, name.length)), createSimpleExpression(value ? value.content : '', isStatic, value ? value.loc : loc)));
@@ -3545,7 +3648,10 @@ function buildProps(node, context, props = node.props, ssr = false) {
3545
3648
  }
3546
3649
  // skip v-is and :is on <component>
3547
3650
  if (name === 'is' ||
3548
- (isVBind && isComponentTag(tag) && isBindKey(arg, 'is'))) {
3651
+ (isVBind &&
3652
+ isBindKey(arg, 'is') &&
3653
+ (isComponentTag(tag) ||
3654
+ (isCompatEnabled("COMPILER_IS_ON_ELEMENT" /* COMPILER_IS_ON_ELEMENT */, context))))) {
3549
3655
  continue;
3550
3656
  }
3551
3657
  // skip v-on in SSR compilation
@@ -4089,7 +4195,7 @@ const transformModel = (dir, node, context) => {
4089
4195
  // _unref(exp)
4090
4196
  context.bindingMetadata[rawExp];
4091
4197
  const maybeRef = !true /* SETUP_CONST */;
4092
- if (!isMemberExpression(expString) && !maybeRef) {
4198
+ if (!expString.trim() || (!isMemberExpression(expString) && !maybeRef)) {
4093
4199
  context.onError(createCompilerError(41 /* X_V_MODEL_MALFORMED_EXPRESSION */, exp.loc));
4094
4200
  return createTransformProps();
4095
4201
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vue/compiler-core",
3
- "version": "3.1.1",
3
+ "version": "3.1.5",
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.1",
35
+ "@vue/shared": "3.1.5",
36
36
  "@babel/parser": "^7.12.0",
37
37
  "@babel/types": "^7.12.0",
38
38
  "estree-walker": "^2.0.1",