@vue/compiler-vapor 3.6.0-beta.1 → 3.6.0-beta.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.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @vue/compiler-vapor v3.6.0-beta.1
2
+ * @vue/compiler-vapor v3.6.0-beta.2
3
3
  * (c) 2018-present Yuxi (Evan) You and Vue contributors
4
4
  * @license MIT
5
5
  **/
@@ -149,6 +149,8 @@ class TransformContext {
149
149
  this.node = node;
150
150
  this.selfName = null;
151
151
  this.parent = null;
152
+ // cached parent that skips template tags
153
+ this.effectiveParent = null;
152
154
  this.index = 0;
153
155
  this.block = this.ir.block;
154
156
  this.template = "";
@@ -161,6 +163,12 @@ class TransformContext {
161
163
  this.component = this.ir.component;
162
164
  this.directive = this.ir.directive;
163
165
  this.slots = [];
166
+ // whether this node is the last effective child of its parent
167
+ // (all siblings after it are components, which don't appear in HTML template)
168
+ this.isLastEffectiveChild = true;
169
+ // whether this node is on the rightmost path of the tree
170
+ // (all ancestors are also last effective children)
171
+ this.isOnRightmostPath = true;
164
172
  this.globalId = 0;
165
173
  this.nextIdMap = null;
166
174
  this.increaseId = () => {
@@ -241,15 +249,31 @@ class TransformContext {
241
249
  this.block.operation.push(...node);
242
250
  }
243
251
  create(node, index) {
252
+ let effectiveParent = this;
253
+ while (effectiveParent && effectiveParent.node.type === 1 && effectiveParent.node.tagType === 3) {
254
+ effectiveParent = effectiveParent.parent;
255
+ }
256
+ const isLastEffectiveChild = this.isEffectivelyLastChild(index);
257
+ const isOnRightmostPath = this.isOnRightmostPath && isLastEffectiveChild;
244
258
  return Object.assign(Object.create(TransformContext.prototype), this, {
245
259
  node,
246
260
  parent: this,
247
261
  index,
248
262
  template: "",
249
263
  childrenTemplate: [],
250
- dynamic: newDynamic()
264
+ dynamic: newDynamic(),
265
+ effectiveParent,
266
+ isLastEffectiveChild,
267
+ isOnRightmostPath
251
268
  });
252
269
  }
270
+ isEffectivelyLastChild(index) {
271
+ const children = this.node.children;
272
+ if (!children) return true;
273
+ return children.every(
274
+ (c, i) => i <= index || c.type === 1 && c.tagType === 1
275
+ );
276
+ }
253
277
  }
254
278
  const defaultOptions = {
255
279
  filename: "",
@@ -789,6 +813,10 @@ function analyzeExpressions(expressions) {
789
813
  end: id.end
790
814
  });
791
815
  });
816
+ const parentOfMemberExp = parentStack[parentStack.length - 2];
817
+ if (parentOfMemberExp && isCallExpression(parentOfMemberExp)) {
818
+ return;
819
+ }
792
820
  registerVariable(
793
821
  memberExp,
794
822
  exp,
@@ -1005,6 +1033,8 @@ function extractMemberExpression(exp, onIdentifier) {
1005
1033
  return `${extractMemberExpression(exp.left, onIdentifier)} ${exp.operator} ${extractMemberExpression(exp.right, onIdentifier)}`;
1006
1034
  case "CallExpression":
1007
1035
  return `${extractMemberExpression(exp.callee, onIdentifier)}(${exp.arguments.map((arg) => extractMemberExpression(arg, onIdentifier)).join(", ")})`;
1036
+ case "OptionalCallExpression":
1037
+ return `${extractMemberExpression(exp.callee, onIdentifier)}?.(${exp.arguments.map((arg) => extractMemberExpression(arg, onIdentifier)).join(", ")})`;
1008
1038
  case "MemberExpression":
1009
1039
  // foo[bar.baz]
1010
1040
  case "OptionalMemberExpression":
@@ -1017,6 +1047,9 @@ function extractMemberExpression(exp, onIdentifier) {
1017
1047
  return "";
1018
1048
  }
1019
1049
  }
1050
+ const isCallExpression = (node) => {
1051
+ return node.type === "CallExpression" || node.type === "OptionalCallExpression";
1052
+ };
1020
1053
  const isMemberExpression = (node) => {
1021
1054
  return node.type === "MemberExpression" || node.type === "OptionalMemberExpression" || node.type === "TSNonNullExpression";
1022
1055
  };
@@ -2996,7 +3029,10 @@ const transformElement = (node, context) => {
2996
3029
  propsResult,
2997
3030
  singleRoot,
2998
3031
  context,
2999
- getEffectIndex
3032
+ getEffectIndex,
3033
+ // Root-level elements generate dedicated templates
3034
+ // so closing tags can be omitted
3035
+ context.root === context.effectiveParent || canOmitEndTag(node, context)
3000
3036
  );
3001
3037
  }
3002
3038
  if (parentSlots) {
@@ -3004,6 +3040,17 @@ const transformElement = (node, context) => {
3004
3040
  }
3005
3041
  };
3006
3042
  };
3043
+ function canOmitEndTag(node, context) {
3044
+ const { block, parent } = context;
3045
+ if (!parent) return false;
3046
+ if (block !== parent.block) {
3047
+ return true;
3048
+ }
3049
+ if (shared.isFormattingTag(node.tag) || parent.node.type === 1 && node.tag === parent.node.tag) {
3050
+ return context.isOnRightmostPath;
3051
+ }
3052
+ return context.isLastEffectiveChild;
3053
+ }
3007
3054
  function isSingleRoot(context) {
3008
3055
  if (context.inVFor) {
3009
3056
  return false;
@@ -3093,7 +3140,8 @@ function resolveSetupReference(name, context) {
3093
3140
  return bindings[name] ? name : bindings[camelName] ? camelName : bindings[PascalName] ? PascalName : void 0;
3094
3141
  }
3095
3142
  const dynamicKeys = ["indeterminate"];
3096
- function transformNativeElement(node, propsResult, singleRoot, context, getEffectIndex) {
3143
+ const NEEDS_QUOTES_RE = /[\s"'`=<>]/;
3144
+ function transformNativeElement(node, propsResult, singleRoot, context, getEffectIndex, omitEndTag) {
3097
3145
  const { tag } = node;
3098
3146
  const { scopeId } = context.options;
3099
3147
  let template = "";
@@ -3113,16 +3161,24 @@ function transformNativeElement(node, propsResult, singleRoot, context, getEffec
3113
3161
  getEffectIndex
3114
3162
  );
3115
3163
  } else {
3164
+ let prevWasQuoted = false;
3116
3165
  for (const prop of propsResult[1]) {
3117
3166
  const { key, values } = prop;
3118
3167
  if (context.imports.some(
3119
3168
  (imported) => values[0].content.includes(imported.exp.content)
3120
3169
  )) {
3121
- template += ` ${key.content}="${IMPORT_EXP_START}${values[0].content}${IMPORT_EXP_END}"`;
3170
+ if (!prevWasQuoted) template += ` `;
3171
+ template += `${key.content}="${IMPORT_EXP_START}${values[0].content}${IMPORT_EXP_END}"`;
3172
+ prevWasQuoted = true;
3122
3173
  } else if (key.isStatic && values.length === 1 && (values[0].isStatic || values[0].content === "''") && !dynamicKeys.includes(key.content)) {
3123
- template += ` ${key.content}`;
3124
- if (values[0].content)
3125
- template += `="${values[0].content === "''" ? "" : values[0].content}"`;
3174
+ if (!prevWasQuoted) template += ` `;
3175
+ const value = values[0].content === "''" ? "" : values[0].content;
3176
+ template += key.content;
3177
+ if (value) {
3178
+ template += (prevWasQuoted = NEEDS_QUOTES_RE.test(value)) ? `="${value.replace(/"/g, "&quot;")}"` : `=${value}`;
3179
+ } else {
3180
+ prevWasQuoted = false;
3181
+ }
3126
3182
  } else {
3127
3183
  dynamicProps.push(key.content);
3128
3184
  context.registerEffect(
@@ -3139,7 +3195,7 @@ function transformNativeElement(node, propsResult, singleRoot, context, getEffec
3139
3195
  }
3140
3196
  }
3141
3197
  template += `>` + context.childrenTemplate.join("");
3142
- if (!shared.isVoidTag(tag)) {
3198
+ if (!shared.isVoidTag(tag) && !omitEndTag) {
3143
3199
  template += `</${tag}>`;
3144
3200
  }
3145
3201
  if (singleRoot) {
@@ -314,6 +314,7 @@ export declare class TransformContext<T extends AllNode = AllNode> {
314
314
  node: T;
315
315
  selfName: string | null;
316
316
  parent: TransformContext<RootNode | ElementNode> | null;
317
+ effectiveParent: TransformContext<RootNode | ElementNode> | null;
317
318
  root: TransformContext<RootNode>;
318
319
  index: number;
319
320
  block: BlockIRNode;
@@ -328,6 +329,8 @@ export declare class TransformContext<T extends AllNode = AllNode> {
328
329
  component: Set<string>;
329
330
  directive: Set<string>;
330
331
  slots: IRSlots[];
332
+ isLastEffectiveChild: boolean;
333
+ isOnRightmostPath: boolean;
331
334
  private globalId;
332
335
  private nextIdMap;
333
336
  constructor(ir: RootIRNode, node: T, options?: TransformOptions);
@@ -340,6 +343,7 @@ export declare class TransformContext<T extends AllNode = AllNode> {
340
343
  registerEffect(expressions: SimpleExpressionNode[], operation: OperationNode | OperationNode[], getIndex?: () => number): void;
341
344
  registerOperation(...node: OperationNode[]): void;
342
345
  create<T extends TemplateChildNode>(node: T, index: number): TransformContext<T>;
346
+ private isEffectivelyLastChild;
343
347
  }
344
348
  export declare function transform(node: RootNode, options?: TransformOptions): RootIRNode;
345
349
  export declare function createStructuralDirectiveTransform(name: string | string[], fn: StructuralDirectiveTransform): NodeTransform;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @vue/compiler-vapor v3.6.0-beta.1
2
+ * @vue/compiler-vapor v3.6.0-beta.2
3
3
  * (c) 2018-present Yuxi (Evan) You and Vue contributors
4
4
  * @license MIT
5
5
  **/
@@ -66,10 +66,12 @@ const HTML_TAGS = "html,body,base,head,link,meta,style,title,address,article,asi
66
66
  const SVG_TAGS = "svg,animate,animateMotion,animateTransform,circle,clipPath,color-profile,defs,desc,discard,ellipse,feBlend,feColorMatrix,feComponentTransfer,feComposite,feConvolveMatrix,feDiffuseLighting,feDisplacementMap,feDistantLight,feDropShadow,feFlood,feFuncA,feFuncB,feFuncG,feFuncR,feGaussianBlur,feImage,feMerge,feMergeNode,feMorphology,feOffset,fePointLight,feSpecularLighting,feSpotLight,feTile,feTurbulence,filter,foreignObject,g,hatch,hatchpath,image,line,linearGradient,marker,mask,mesh,meshgradient,meshpatch,meshrow,metadata,mpath,path,pattern,polygon,polyline,radialGradient,rect,set,solidcolor,stop,switch,symbol,text,textPath,title,tspan,unknown,use,view";
67
67
  const MATH_TAGS = "annotation,annotation-xml,maction,maligngroup,malignmark,math,menclose,merror,mfenced,mfrac,mfraction,mglyph,mi,mlabeledtr,mlongdiv,mmultiscripts,mn,mo,mover,mpadded,mphantom,mprescripts,mroot,mrow,ms,mscarries,mscarry,msgroup,msline,mspace,msqrt,msrow,mstack,mstyle,msub,msubsup,msup,mtable,mtd,mtext,mtr,munder,munderover,none,semantics";
68
68
  const VOID_TAGS = "area,base,br,col,embed,hr,img,input,link,meta,param,source,track,wbr";
69
+ const FORMATTING_TAGS = "a,b,big,code,em,font,i,nobr,s,small,strike,strong,tt,u";
69
70
  const isHTMLTag = /* @__PURE__ */ makeMap(HTML_TAGS);
70
71
  const isSVGTag = /* @__PURE__ */ makeMap(SVG_TAGS);
71
72
  const isMathMLTag = /* @__PURE__ */ makeMap(MATH_TAGS);
72
73
  const isVoidTag = /* @__PURE__ */ makeMap(VOID_TAGS);
74
+ const isFormattingTag = /* @__PURE__ */ makeMap(FORMATTING_TAGS);
73
75
 
74
76
  function shouldSetAsAttr(tagName, key) {
75
77
  if (key === "spellcheck" || key === "draggable" || key === "translate" || key === "autocorrect") {
@@ -18629,8 +18631,7 @@ function walkIdentifiers(root, onIdentifier, includeAll = false, parentStack = [
18629
18631
  if (includeAll || isRefed && !isLocal) {
18630
18632
  onIdentifier(node, parent, parentStack, isRefed, isLocal);
18631
18633
  }
18632
- } else if (node.type === "ObjectProperty" && // eslint-disable-next-line no-restricted-syntax
18633
- (parent == null ? void 0 : parent.type) === "ObjectPattern") {
18634
+ } else if (node.type === "ObjectProperty" && (parent == null ? void 0 : parent.type) === "ObjectPattern") {
18634
18635
  node.inPattern = true;
18635
18636
  } else if (isFunctionType(node)) {
18636
18637
  if (node.scopeIds) {
@@ -23810,6 +23811,8 @@ class TransformContext {
23810
23811
  this.node = node;
23811
23812
  this.selfName = null;
23812
23813
  this.parent = null;
23814
+ // cached parent that skips template tags
23815
+ this.effectiveParent = null;
23813
23816
  this.index = 0;
23814
23817
  this.block = this.ir.block;
23815
23818
  this.template = "";
@@ -23822,6 +23825,12 @@ class TransformContext {
23822
23825
  this.component = this.ir.component;
23823
23826
  this.directive = this.ir.directive;
23824
23827
  this.slots = [];
23828
+ // whether this node is the last effective child of its parent
23829
+ // (all siblings after it are components, which don't appear in HTML template)
23830
+ this.isLastEffectiveChild = true;
23831
+ // whether this node is on the rightmost path of the tree
23832
+ // (all ancestors are also last effective children)
23833
+ this.isOnRightmostPath = true;
23825
23834
  this.globalId = 0;
23826
23835
  this.nextIdMap = null;
23827
23836
  this.increaseId = () => {
@@ -23902,15 +23911,31 @@ class TransformContext {
23902
23911
  this.block.operation.push(...node);
23903
23912
  }
23904
23913
  create(node, index) {
23914
+ let effectiveParent = this;
23915
+ while (effectiveParent && effectiveParent.node.type === 1 && effectiveParent.node.tagType === 3) {
23916
+ effectiveParent = effectiveParent.parent;
23917
+ }
23918
+ const isLastEffectiveChild = this.isEffectivelyLastChild(index);
23919
+ const isOnRightmostPath = this.isOnRightmostPath && isLastEffectiveChild;
23905
23920
  return Object.assign(Object.create(TransformContext.prototype), this, {
23906
23921
  node,
23907
23922
  parent: this,
23908
23923
  index,
23909
23924
  template: "",
23910
23925
  childrenTemplate: [],
23911
- dynamic: newDynamic()
23926
+ dynamic: newDynamic(),
23927
+ effectiveParent,
23928
+ isLastEffectiveChild,
23929
+ isOnRightmostPath
23912
23930
  });
23913
23931
  }
23932
+ isEffectivelyLastChild(index) {
23933
+ const children = this.node.children;
23934
+ if (!children) return true;
23935
+ return children.every(
23936
+ (c, i) => i <= index || c.type === 1 && c.tagType === 1
23937
+ );
23938
+ }
23914
23939
  }
23915
23940
  const defaultOptions = {
23916
23941
  filename: "",
@@ -24450,6 +24475,10 @@ function analyzeExpressions(expressions) {
24450
24475
  end: id.end
24451
24476
  });
24452
24477
  });
24478
+ const parentOfMemberExp = parentStack[parentStack.length - 2];
24479
+ if (parentOfMemberExp && isCallExpression(parentOfMemberExp)) {
24480
+ return;
24481
+ }
24453
24482
  registerVariable(
24454
24483
  memberExp,
24455
24484
  exp,
@@ -24666,6 +24695,8 @@ function extractMemberExpression(exp, onIdentifier) {
24666
24695
  return `${extractMemberExpression(exp.left, onIdentifier)} ${exp.operator} ${extractMemberExpression(exp.right, onIdentifier)}`;
24667
24696
  case "CallExpression":
24668
24697
  return `${extractMemberExpression(exp.callee, onIdentifier)}(${exp.arguments.map((arg) => extractMemberExpression(arg, onIdentifier)).join(", ")})`;
24698
+ case "OptionalCallExpression":
24699
+ return `${extractMemberExpression(exp.callee, onIdentifier)}?.(${exp.arguments.map((arg) => extractMemberExpression(arg, onIdentifier)).join(", ")})`;
24669
24700
  case "MemberExpression":
24670
24701
  // foo[bar.baz]
24671
24702
  case "OptionalMemberExpression":
@@ -24678,6 +24709,9 @@ function extractMemberExpression(exp, onIdentifier) {
24678
24709
  return "";
24679
24710
  }
24680
24711
  }
24712
+ const isCallExpression = (node) => {
24713
+ return node.type === "CallExpression" || node.type === "OptionalCallExpression";
24714
+ };
24681
24715
  const isMemberExpression = (node) => {
24682
24716
  return node.type === "MemberExpression" || node.type === "OptionalMemberExpression" || node.type === "TSNonNullExpression";
24683
24717
  };
@@ -26657,7 +26691,10 @@ const transformElement = (node, context) => {
26657
26691
  propsResult,
26658
26692
  singleRoot,
26659
26693
  context,
26660
- getEffectIndex
26694
+ getEffectIndex,
26695
+ // Root-level elements generate dedicated templates
26696
+ // so closing tags can be omitted
26697
+ context.root === context.effectiveParent || canOmitEndTag(node, context)
26661
26698
  );
26662
26699
  }
26663
26700
  if (parentSlots) {
@@ -26665,6 +26702,17 @@ const transformElement = (node, context) => {
26665
26702
  }
26666
26703
  };
26667
26704
  };
26705
+ function canOmitEndTag(node, context) {
26706
+ const { block, parent } = context;
26707
+ if (!parent) return false;
26708
+ if (block !== parent.block) {
26709
+ return true;
26710
+ }
26711
+ if (isFormattingTag(node.tag) || parent.node.type === 1 && node.tag === parent.node.tag) {
26712
+ return context.isOnRightmostPath;
26713
+ }
26714
+ return context.isLastEffectiveChild;
26715
+ }
26668
26716
  function isSingleRoot(context) {
26669
26717
  if (context.inVFor) {
26670
26718
  return false;
@@ -26754,7 +26802,8 @@ function resolveSetupReference(name, context) {
26754
26802
  return bindings[name] ? name : bindings[camelName] ? camelName : bindings[PascalName] ? PascalName : void 0;
26755
26803
  }
26756
26804
  const dynamicKeys = ["indeterminate"];
26757
- function transformNativeElement(node, propsResult, singleRoot, context, getEffectIndex) {
26805
+ const NEEDS_QUOTES_RE = /[\s"'`=<>]/;
26806
+ function transformNativeElement(node, propsResult, singleRoot, context, getEffectIndex, omitEndTag) {
26758
26807
  const { tag } = node;
26759
26808
  const { scopeId } = context.options;
26760
26809
  let template = "";
@@ -26774,16 +26823,24 @@ function transformNativeElement(node, propsResult, singleRoot, context, getEffec
26774
26823
  getEffectIndex
26775
26824
  );
26776
26825
  } else {
26826
+ let prevWasQuoted = false;
26777
26827
  for (const prop of propsResult[1]) {
26778
26828
  const { key, values } = prop;
26779
26829
  if (context.imports.some(
26780
26830
  (imported) => values[0].content.includes(imported.exp.content)
26781
26831
  )) {
26782
- template += ` ${key.content}="${IMPORT_EXP_START}${values[0].content}${IMPORT_EXP_END}"`;
26832
+ if (!prevWasQuoted) template += ` `;
26833
+ template += `${key.content}="${IMPORT_EXP_START}${values[0].content}${IMPORT_EXP_END}"`;
26834
+ prevWasQuoted = true;
26783
26835
  } else if (key.isStatic && values.length === 1 && (values[0].isStatic || values[0].content === "''") && !dynamicKeys.includes(key.content)) {
26784
- template += ` ${key.content}`;
26785
- if (values[0].content)
26786
- template += `="${values[0].content === "''" ? "" : values[0].content}"`;
26836
+ if (!prevWasQuoted) template += ` `;
26837
+ const value = values[0].content === "''" ? "" : values[0].content;
26838
+ template += key.content;
26839
+ if (value) {
26840
+ template += (prevWasQuoted = NEEDS_QUOTES_RE.test(value)) ? `="${value.replace(/"/g, "&quot;")}"` : `=${value}`;
26841
+ } else {
26842
+ prevWasQuoted = false;
26843
+ }
26787
26844
  } else {
26788
26845
  dynamicProps.push(key.content);
26789
26846
  context.registerEffect(
@@ -26800,7 +26857,7 @@ function transformNativeElement(node, propsResult, singleRoot, context, getEffec
26800
26857
  }
26801
26858
  }
26802
26859
  template += `>` + context.childrenTemplate.join("");
26803
- if (!isVoidTag(tag)) {
26860
+ if (!isVoidTag(tag) && !omitEndTag) {
26804
26861
  template += `</${tag}>`;
26805
26862
  }
26806
26863
  if (singleRoot) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vue/compiler-vapor",
3
- "version": "3.6.0-beta.1",
3
+ "version": "3.6.0-beta.2",
4
4
  "description": "@vue/compiler-vapor",
5
5
  "main": "dist/compiler-vapor.cjs.js",
6
6
  "module": "dist/compiler-vapor.esm-bundler.js",
@@ -45,7 +45,7 @@
45
45
  "@babel/parser": "^7.28.5",
46
46
  "estree-walker": "^2.0.2",
47
47
  "source-map-js": "^1.2.1",
48
- "@vue/compiler-dom": "3.6.0-beta.1",
49
- "@vue/shared": "3.6.0-beta.1"
48
+ "@vue/compiler-dom": "3.6.0-beta.2",
49
+ "@vue/shared": "3.6.0-beta.2"
50
50
  }
51
51
  }