refactor(compiler): better constant hoist/stringify checks
This commit is contained in:
		
							parent
							
								
									acba86ef45
								
							
						
					
					
						commit
						90bdf59f4c
					
				@ -3322,8 +3322,8 @@ Object {
 | 
			
		||||
      "children": Array [
 | 
			
		||||
        Object {
 | 
			
		||||
          "content": Object {
 | 
			
		||||
            "constType": 0,
 | 
			
		||||
            "content": "a < b",
 | 
			
		||||
            "isConstant": false,
 | 
			
		||||
            "isStatic": false,
 | 
			
		||||
            "loc": Object {
 | 
			
		||||
              "end": Object {
 | 
			
		||||
@ -6084,8 +6084,8 @@ Object {
 | 
			
		||||
      "children": Array [
 | 
			
		||||
        Object {
 | 
			
		||||
          "content": Object {
 | 
			
		||||
            "constType": 0,
 | 
			
		||||
            "content": "'</div>'",
 | 
			
		||||
            "isConstant": false,
 | 
			
		||||
            "isStatic": false,
 | 
			
		||||
            "loc": Object {
 | 
			
		||||
              "end": Object {
 | 
			
		||||
@ -6259,8 +6259,8 @@ Object {
 | 
			
		||||
      "props": Array [
 | 
			
		||||
        Object {
 | 
			
		||||
          "arg": Object {
 | 
			
		||||
            "constType": 0,
 | 
			
		||||
            "content": "se",
 | 
			
		||||
            "isConstant": false,
 | 
			
		||||
            "isStatic": false,
 | 
			
		||||
            "loc": Object {
 | 
			
		||||
              "end": Object {
 | 
			
		||||
@ -6593,8 +6593,8 @@ Object {
 | 
			
		||||
  "children": Array [
 | 
			
		||||
    Object {
 | 
			
		||||
      "content": Object {
 | 
			
		||||
        "constType": 0,
 | 
			
		||||
        "content": "",
 | 
			
		||||
        "isConstant": false,
 | 
			
		||||
        "isStatic": false,
 | 
			
		||||
        "loc": Object {
 | 
			
		||||
          "end": Object {
 | 
			
		||||
@ -6758,8 +6758,8 @@ Object {
 | 
			
		||||
      "props": Array [
 | 
			
		||||
        Object {
 | 
			
		||||
          "arg": Object {
 | 
			
		||||
            "constType": 3,
 | 
			
		||||
            "content": "class",
 | 
			
		||||
            "isConstant": true,
 | 
			
		||||
            "isStatic": true,
 | 
			
		||||
            "loc": Object {
 | 
			
		||||
              "end": Object {
 | 
			
		||||
@ -6777,8 +6777,8 @@ Object {
 | 
			
		||||
            "type": 4,
 | 
			
		||||
          },
 | 
			
		||||
          "exp": Object {
 | 
			
		||||
            "constType": 0,
 | 
			
		||||
            "content": "{ some: condition }",
 | 
			
		||||
            "isConstant": false,
 | 
			
		||||
            "isStatic": false,
 | 
			
		||||
            "loc": Object {
 | 
			
		||||
              "end": Object {
 | 
			
		||||
@ -6838,8 +6838,8 @@ Object {
 | 
			
		||||
      "props": Array [
 | 
			
		||||
        Object {
 | 
			
		||||
          "arg": Object {
 | 
			
		||||
            "constType": 3,
 | 
			
		||||
            "content": "style",
 | 
			
		||||
            "isConstant": true,
 | 
			
		||||
            "isStatic": true,
 | 
			
		||||
            "loc": Object {
 | 
			
		||||
              "end": Object {
 | 
			
		||||
@ -6857,8 +6857,8 @@ Object {
 | 
			
		||||
            "type": 4,
 | 
			
		||||
          },
 | 
			
		||||
          "exp": Object {
 | 
			
		||||
            "constType": 0,
 | 
			
		||||
            "content": "{ color: 'red' }",
 | 
			
		||||
            "isConstant": false,
 | 
			
		||||
            "isStatic": false,
 | 
			
		||||
            "loc": Object {
 | 
			
		||||
              "end": Object {
 | 
			
		||||
@ -6950,8 +6950,8 @@ Object {
 | 
			
		||||
          "props": Array [
 | 
			
		||||
            Object {
 | 
			
		||||
              "arg": Object {
 | 
			
		||||
                "constType": 3,
 | 
			
		||||
                "content": "style",
 | 
			
		||||
                "isConstant": true,
 | 
			
		||||
                "isStatic": true,
 | 
			
		||||
                "loc": Object {
 | 
			
		||||
                  "end": Object {
 | 
			
		||||
@ -6969,8 +6969,8 @@ Object {
 | 
			
		||||
                "type": 4,
 | 
			
		||||
              },
 | 
			
		||||
              "exp": Object {
 | 
			
		||||
                "constType": 0,
 | 
			
		||||
                "content": "{ color: 'red' }",
 | 
			
		||||
                "isConstant": false,
 | 
			
		||||
                "isStatic": false,
 | 
			
		||||
                "loc": Object {
 | 
			
		||||
                  "end": Object {
 | 
			
		||||
@ -7049,8 +7049,8 @@ Object {
 | 
			
		||||
      "props": Array [
 | 
			
		||||
        Object {
 | 
			
		||||
          "arg": Object {
 | 
			
		||||
            "constType": 3,
 | 
			
		||||
            "content": "class",
 | 
			
		||||
            "isConstant": true,
 | 
			
		||||
            "isStatic": true,
 | 
			
		||||
            "loc": Object {
 | 
			
		||||
              "end": Object {
 | 
			
		||||
@ -7068,8 +7068,8 @@ Object {
 | 
			
		||||
            "type": 4,
 | 
			
		||||
          },
 | 
			
		||||
          "exp": Object {
 | 
			
		||||
            "constType": 0,
 | 
			
		||||
            "content": "{ some: condition }",
 | 
			
		||||
            "isConstant": false,
 | 
			
		||||
            "isStatic": false,
 | 
			
		||||
            "loc": Object {
 | 
			
		||||
              "end": Object {
 | 
			
		||||
 | 
			
		||||
@ -20,7 +20,8 @@ import {
 | 
			
		||||
  IfConditionalExpression,
 | 
			
		||||
  createVNodeCall,
 | 
			
		||||
  VNodeCall,
 | 
			
		||||
  DirectiveArguments
 | 
			
		||||
  DirectiveArguments,
 | 
			
		||||
  ConstantTypes
 | 
			
		||||
} from '../src'
 | 
			
		||||
import {
 | 
			
		||||
  CREATE_VNODE,
 | 
			
		||||
@ -304,7 +305,12 @@ describe('compiler: codegen', () => {
 | 
			
		||||
        codegenNode: {
 | 
			
		||||
          type: NodeTypes.FOR,
 | 
			
		||||
          loc: locStub,
 | 
			
		||||
          source: createSimpleExpression('1 + 2', false, locStub, true),
 | 
			
		||||
          source: createSimpleExpression(
 | 
			
		||||
            '1 + 2',
 | 
			
		||||
            false,
 | 
			
		||||
            locStub,
 | 
			
		||||
            ConstantTypes.CAN_STRINGIFY
 | 
			
		||||
          ),
 | 
			
		||||
          valueAlias: undefined,
 | 
			
		||||
          keyAlias: undefined,
 | 
			
		||||
          objectIndexAlias: undefined,
 | 
			
		||||
 | 
			
		||||
@ -9,7 +9,8 @@ import {
 | 
			
		||||
  NodeTypes,
 | 
			
		||||
  Position,
 | 
			
		||||
  TextNode,
 | 
			
		||||
  InterpolationNode
 | 
			
		||||
  InterpolationNode,
 | 
			
		||||
  ConstantTypes
 | 
			
		||||
} from '../src/ast'
 | 
			
		||||
 | 
			
		||||
describe('compiler: parse', () => {
 | 
			
		||||
@ -177,7 +178,7 @@ describe('compiler: parse', () => {
 | 
			
		||||
          type: NodeTypes.SIMPLE_EXPRESSION,
 | 
			
		||||
          content: `message`,
 | 
			
		||||
          isStatic: false,
 | 
			
		||||
          isConstant: false,
 | 
			
		||||
          constType: ConstantTypes.NOT_CONSTANT,
 | 
			
		||||
          loc: {
 | 
			
		||||
            start: { offset: 2, line: 1, column: 3 },
 | 
			
		||||
            end: { offset: 9, line: 1, column: 10 },
 | 
			
		||||
@ -202,7 +203,7 @@ describe('compiler: parse', () => {
 | 
			
		||||
          type: NodeTypes.SIMPLE_EXPRESSION,
 | 
			
		||||
          content: `a<b`,
 | 
			
		||||
          isStatic: false,
 | 
			
		||||
          isConstant: false,
 | 
			
		||||
          constType: ConstantTypes.NOT_CONSTANT,
 | 
			
		||||
          loc: {
 | 
			
		||||
            start: { offset: 3, line: 1, column: 4 },
 | 
			
		||||
            end: { offset: 6, line: 1, column: 7 },
 | 
			
		||||
@ -228,7 +229,7 @@ describe('compiler: parse', () => {
 | 
			
		||||
          type: NodeTypes.SIMPLE_EXPRESSION,
 | 
			
		||||
          content: `a<b`,
 | 
			
		||||
          isStatic: false,
 | 
			
		||||
          isConstant: false,
 | 
			
		||||
          constType: ConstantTypes.NOT_CONSTANT,
 | 
			
		||||
          loc: {
 | 
			
		||||
            start: { offset: 3, line: 1, column: 4 },
 | 
			
		||||
            end: { offset: 6, line: 1, column: 7 },
 | 
			
		||||
@ -247,7 +248,7 @@ describe('compiler: parse', () => {
 | 
			
		||||
        content: {
 | 
			
		||||
          type: NodeTypes.SIMPLE_EXPRESSION,
 | 
			
		||||
          isStatic: false,
 | 
			
		||||
          isConstant: false,
 | 
			
		||||
          constType: ConstantTypes.NOT_CONSTANT,
 | 
			
		||||
          content: 'c>d',
 | 
			
		||||
          loc: {
 | 
			
		||||
            start: { offset: 12, line: 1, column: 13 },
 | 
			
		||||
@ -273,8 +274,8 @@ describe('compiler: parse', () => {
 | 
			
		||||
        content: {
 | 
			
		||||
          type: NodeTypes.SIMPLE_EXPRESSION,
 | 
			
		||||
          isStatic: false,
 | 
			
		||||
          // The `isConstant` is the default value and will be determined in `transformExpression`.
 | 
			
		||||
          isConstant: false,
 | 
			
		||||
          // The `constType` is the default value and will be determined in `transformExpression`.
 | 
			
		||||
          constType: ConstantTypes.NOT_CONSTANT,
 | 
			
		||||
          content: '"</div>"',
 | 
			
		||||
          loc: {
 | 
			
		||||
            start: { offset: 8, line: 1, column: 9 },
 | 
			
		||||
@ -303,7 +304,7 @@ describe('compiler: parse', () => {
 | 
			
		||||
          type: NodeTypes.SIMPLE_EXPRESSION,
 | 
			
		||||
          content: `msg`,
 | 
			
		||||
          isStatic: false,
 | 
			
		||||
          isConstant: false,
 | 
			
		||||
          constType: ConstantTypes.NOT_CONSTANT,
 | 
			
		||||
          loc: {
 | 
			
		||||
            start: { offset: 4, line: 1, column: 5 },
 | 
			
		||||
            end: { offset: 7, line: 1, column: 8 },
 | 
			
		||||
@ -1028,7 +1029,7 @@ describe('compiler: parse', () => {
 | 
			
		||||
          type: NodeTypes.SIMPLE_EXPRESSION,
 | 
			
		||||
          content: 'a',
 | 
			
		||||
          isStatic: false,
 | 
			
		||||
          isConstant: false,
 | 
			
		||||
          constType: ConstantTypes.NOT_CONSTANT,
 | 
			
		||||
          loc: {
 | 
			
		||||
            start: { offset: 11, line: 1, column: 12 },
 | 
			
		||||
            end: { offset: 12, line: 1, column: 13 },
 | 
			
		||||
@ -1054,7 +1055,7 @@ describe('compiler: parse', () => {
 | 
			
		||||
          type: NodeTypes.SIMPLE_EXPRESSION,
 | 
			
		||||
          content: 'click',
 | 
			
		||||
          isStatic: true,
 | 
			
		||||
          isConstant: true,
 | 
			
		||||
          constType: ConstantTypes.CAN_STRINGIFY,
 | 
			
		||||
 | 
			
		||||
          loc: {
 | 
			
		||||
            source: 'click',
 | 
			
		||||
@ -1091,7 +1092,7 @@ describe('compiler: parse', () => {
 | 
			
		||||
          type: NodeTypes.SIMPLE_EXPRESSION,
 | 
			
		||||
          content: 'event',
 | 
			
		||||
          isStatic: false,
 | 
			
		||||
          isConstant: false,
 | 
			
		||||
          constType: ConstantTypes.NOT_CONSTANT,
 | 
			
		||||
 | 
			
		||||
          loc: {
 | 
			
		||||
            source: '[event]',
 | 
			
		||||
@ -1164,7 +1165,7 @@ describe('compiler: parse', () => {
 | 
			
		||||
          type: NodeTypes.SIMPLE_EXPRESSION,
 | 
			
		||||
          content: 'click',
 | 
			
		||||
          isStatic: true,
 | 
			
		||||
          isConstant: true,
 | 
			
		||||
          constType: ConstantTypes.CAN_STRINGIFY,
 | 
			
		||||
 | 
			
		||||
          loc: {
 | 
			
		||||
            source: 'click',
 | 
			
		||||
@ -1201,7 +1202,7 @@ describe('compiler: parse', () => {
 | 
			
		||||
          type: NodeTypes.SIMPLE_EXPRESSION,
 | 
			
		||||
          content: 'a.b',
 | 
			
		||||
          isStatic: false,
 | 
			
		||||
          isConstant: false,
 | 
			
		||||
          constType: ConstantTypes.NOT_CONSTANT,
 | 
			
		||||
 | 
			
		||||
          loc: {
 | 
			
		||||
            source: '[a.b]',
 | 
			
		||||
@ -1238,7 +1239,7 @@ describe('compiler: parse', () => {
 | 
			
		||||
          type: NodeTypes.SIMPLE_EXPRESSION,
 | 
			
		||||
          content: 'a',
 | 
			
		||||
          isStatic: true,
 | 
			
		||||
          isConstant: true,
 | 
			
		||||
          constType: ConstantTypes.CAN_STRINGIFY,
 | 
			
		||||
 | 
			
		||||
          loc: {
 | 
			
		||||
            source: 'a',
 | 
			
		||||
@ -1259,7 +1260,7 @@ describe('compiler: parse', () => {
 | 
			
		||||
          type: NodeTypes.SIMPLE_EXPRESSION,
 | 
			
		||||
          content: 'b',
 | 
			
		||||
          isStatic: false,
 | 
			
		||||
          isConstant: false,
 | 
			
		||||
          constType: ConstantTypes.NOT_CONSTANT,
 | 
			
		||||
 | 
			
		||||
          loc: {
 | 
			
		||||
            start: { offset: 8, line: 1, column: 9 },
 | 
			
		||||
@ -1286,7 +1287,7 @@ describe('compiler: parse', () => {
 | 
			
		||||
          type: NodeTypes.SIMPLE_EXPRESSION,
 | 
			
		||||
          content: 'a',
 | 
			
		||||
          isStatic: true,
 | 
			
		||||
          isConstant: true,
 | 
			
		||||
          constType: ConstantTypes.CAN_STRINGIFY,
 | 
			
		||||
 | 
			
		||||
          loc: {
 | 
			
		||||
            source: 'a',
 | 
			
		||||
@ -1307,7 +1308,7 @@ describe('compiler: parse', () => {
 | 
			
		||||
          type: NodeTypes.SIMPLE_EXPRESSION,
 | 
			
		||||
          content: 'b',
 | 
			
		||||
          isStatic: false,
 | 
			
		||||
          isConstant: false,
 | 
			
		||||
          constType: ConstantTypes.NOT_CONSTANT,
 | 
			
		||||
 | 
			
		||||
          loc: {
 | 
			
		||||
            start: { offset: 13, line: 1, column: 14 },
 | 
			
		||||
@ -1334,7 +1335,7 @@ describe('compiler: parse', () => {
 | 
			
		||||
          type: NodeTypes.SIMPLE_EXPRESSION,
 | 
			
		||||
          content: 'a',
 | 
			
		||||
          isStatic: true,
 | 
			
		||||
          isConstant: true,
 | 
			
		||||
          constType: ConstantTypes.CAN_STRINGIFY,
 | 
			
		||||
 | 
			
		||||
          loc: {
 | 
			
		||||
            source: 'a',
 | 
			
		||||
@ -1355,7 +1356,7 @@ describe('compiler: parse', () => {
 | 
			
		||||
          type: NodeTypes.SIMPLE_EXPRESSION,
 | 
			
		||||
          content: 'b',
 | 
			
		||||
          isStatic: false,
 | 
			
		||||
          isConstant: false,
 | 
			
		||||
          constType: ConstantTypes.NOT_CONSTANT,
 | 
			
		||||
 | 
			
		||||
          loc: {
 | 
			
		||||
            start: { offset: 8, line: 1, column: 9 },
 | 
			
		||||
@ -1382,7 +1383,7 @@ describe('compiler: parse', () => {
 | 
			
		||||
          type: NodeTypes.SIMPLE_EXPRESSION,
 | 
			
		||||
          content: 'a',
 | 
			
		||||
          isStatic: true,
 | 
			
		||||
          isConstant: true,
 | 
			
		||||
          constType: ConstantTypes.CAN_STRINGIFY,
 | 
			
		||||
 | 
			
		||||
          loc: {
 | 
			
		||||
            source: 'a',
 | 
			
		||||
@ -1403,7 +1404,7 @@ describe('compiler: parse', () => {
 | 
			
		||||
          type: NodeTypes.SIMPLE_EXPRESSION,
 | 
			
		||||
          content: 'b',
 | 
			
		||||
          isStatic: false,
 | 
			
		||||
          isConstant: false,
 | 
			
		||||
          constType: ConstantTypes.NOT_CONSTANT,
 | 
			
		||||
 | 
			
		||||
          loc: {
 | 
			
		||||
            start: { offset: 14, line: 1, column: 15 },
 | 
			
		||||
@ -1430,7 +1431,7 @@ describe('compiler: parse', () => {
 | 
			
		||||
          type: NodeTypes.SIMPLE_EXPRESSION,
 | 
			
		||||
          content: 'a',
 | 
			
		||||
          isStatic: true,
 | 
			
		||||
          isConstant: true,
 | 
			
		||||
          constType: ConstantTypes.CAN_STRINGIFY,
 | 
			
		||||
          loc: {
 | 
			
		||||
            source: 'a',
 | 
			
		||||
            start: {
 | 
			
		||||
@ -1450,8 +1451,8 @@ describe('compiler: parse', () => {
 | 
			
		||||
          type: NodeTypes.SIMPLE_EXPRESSION,
 | 
			
		||||
          content: '{ b }',
 | 
			
		||||
          isStatic: false,
 | 
			
		||||
          // The `isConstant` is the default value and will be determined in transformExpression
 | 
			
		||||
          isConstant: false,
 | 
			
		||||
          // The `constType` is the default value and will be determined in transformExpression
 | 
			
		||||
          constType: ConstantTypes.NOT_CONSTANT,
 | 
			
		||||
          loc: {
 | 
			
		||||
            start: { offset: 10, line: 1, column: 11 },
 | 
			
		||||
            end: { offset: 15, line: 1, column: 16 },
 | 
			
		||||
@ -1478,7 +1479,7 @@ describe('compiler: parse', () => {
 | 
			
		||||
          type: NodeTypes.SIMPLE_EXPRESSION,
 | 
			
		||||
          content: 'foo.bar',
 | 
			
		||||
          isStatic: true,
 | 
			
		||||
          isConstant: true,
 | 
			
		||||
          constType: ConstantTypes.CAN_STRINGIFY,
 | 
			
		||||
          loc: {
 | 
			
		||||
            source: 'foo.bar',
 | 
			
		||||
            start: {
 | 
			
		||||
 | 
			
		||||
@ -7,7 +7,8 @@ import {
 | 
			
		||||
  VNodeCall,
 | 
			
		||||
  IfNode,
 | 
			
		||||
  ElementNode,
 | 
			
		||||
  ForNode
 | 
			
		||||
  ForNode,
 | 
			
		||||
  ConstantTypes
 | 
			
		||||
} from '../../src'
 | 
			
		||||
import { FRAGMENT, RENDER_LIST, CREATE_TEXT } from '../../src/runtimeHelpers'
 | 
			
		||||
import { transformElement } from '../../src/transforms/transformElement'
 | 
			
		||||
@ -469,7 +470,7 @@ describe('compiler: hoistStatic transform', () => {
 | 
			
		||||
            content: {
 | 
			
		||||
              content: `1`,
 | 
			
		||||
              isStatic: false,
 | 
			
		||||
              isConstant: true
 | 
			
		||||
              constType: ConstantTypes.CAN_STRINGIFY
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
@ -505,13 +506,13 @@ describe('compiler: hoistStatic transform', () => {
 | 
			
		||||
            {
 | 
			
		||||
              key: {
 | 
			
		||||
                content: `class`,
 | 
			
		||||
                isConstant: true,
 | 
			
		||||
                isStatic: true
 | 
			
		||||
                isStatic: true,
 | 
			
		||||
                constType: ConstantTypes.CAN_STRINGIFY
 | 
			
		||||
              },
 | 
			
		||||
              value: {
 | 
			
		||||
                content: `{ foo: true }`,
 | 
			
		||||
                isConstant: true,
 | 
			
		||||
                isStatic: false
 | 
			
		||||
                isStatic: false,
 | 
			
		||||
                constType: ConstantTypes.CAN_STRINGIFY
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          ]
 | 
			
		||||
@ -534,8 +535,8 @@ describe('compiler: hoistStatic transform', () => {
 | 
			
		||||
                type: NodeTypes.INTERPOLATION,
 | 
			
		||||
                content: {
 | 
			
		||||
                  content: `_ctx.bar`,
 | 
			
		||||
                  isConstant: false,
 | 
			
		||||
                  isStatic: false
 | 
			
		||||
                  isStatic: false,
 | 
			
		||||
                  constType: ConstantTypes.NOT_CONSTANT
 | 
			
		||||
                }
 | 
			
		||||
              },
 | 
			
		||||
              patchFlag: `1 /* TEXT */`
 | 
			
		||||
 | 
			
		||||
@ -5,7 +5,8 @@ import {
 | 
			
		||||
  DirectiveNode,
 | 
			
		||||
  NodeTypes,
 | 
			
		||||
  CompilerOptions,
 | 
			
		||||
  InterpolationNode
 | 
			
		||||
  InterpolationNode,
 | 
			
		||||
  ConstantTypes
 | 
			
		||||
} from '../../src'
 | 
			
		||||
import { transformIf } from '../../src/transforms/vIf'
 | 
			
		||||
import { transformExpression } from '../../src/transforms/transformExpression'
 | 
			
		||||
@ -408,7 +409,7 @@ describe('compiler: expression transform', () => {
 | 
			
		||||
        type: NodeTypes.SIMPLE_EXPRESSION,
 | 
			
		||||
        content: `13000n`,
 | 
			
		||||
        isStatic: false,
 | 
			
		||||
        isConstant: true
 | 
			
		||||
        constType: ConstantTypes.CAN_STRINGIFY
 | 
			
		||||
      })
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -12,7 +12,8 @@ import {
 | 
			
		||||
  SimpleExpressionNode,
 | 
			
		||||
  ElementNode,
 | 
			
		||||
  InterpolationNode,
 | 
			
		||||
  ForCodegenNode
 | 
			
		||||
  ForCodegenNode,
 | 
			
		||||
  ConstantTypes
 | 
			
		||||
} from '../../src/ast'
 | 
			
		||||
import { ErrorCodes } from '../../src/errors'
 | 
			
		||||
import { CompilerOptions, generate } from '../../src'
 | 
			
		||||
@ -760,7 +761,7 @@ describe('compiler: v-for', () => {
 | 
			
		||||
          false /* disableTracking */
 | 
			
		||||
        )
 | 
			
		||||
      ).toMatchObject({
 | 
			
		||||
        source: { content: `10`, isConstant: true },
 | 
			
		||||
        source: { content: `10`, constType: ConstantTypes.CAN_STRINGIFY },
 | 
			
		||||
        params: [{ content: `item` }],
 | 
			
		||||
        innerVNodeCall: {
 | 
			
		||||
          tag: `"p"`,
 | 
			
		||||
@ -772,7 +773,7 @@ describe('compiler: v-for', () => {
 | 
			
		||||
              type: NodeTypes.SIMPLE_EXPRESSION,
 | 
			
		||||
              content: 'item',
 | 
			
		||||
              isStatic: false,
 | 
			
		||||
              isConstant: false
 | 
			
		||||
              constType: ConstantTypes.NOT_CONSTANT
 | 
			
		||||
            }
 | 
			
		||||
          },
 | 
			
		||||
          patchFlag: genFlagText(PatchFlags.TEXT)
 | 
			
		||||
 | 
			
		||||
@ -189,11 +189,23 @@ export interface DirectiveNode extends Node {
 | 
			
		||||
  parseResult?: ForParseResult
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Static types have several levels.
 | 
			
		||||
 * Higher levels implies lower levels. e.g. a node that can be stringified
 | 
			
		||||
 * can always be hoisted and skipped for patch.
 | 
			
		||||
 */
 | 
			
		||||
export const enum ConstantTypes {
 | 
			
		||||
  NOT_CONSTANT = 0,
 | 
			
		||||
  CAN_SKIP_PATCH,
 | 
			
		||||
  CAN_HOIST,
 | 
			
		||||
  CAN_STRINGIFY
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface SimpleExpressionNode extends Node {
 | 
			
		||||
  type: NodeTypes.SIMPLE_EXPRESSION
 | 
			
		||||
  content: string
 | 
			
		||||
  isStatic: boolean
 | 
			
		||||
  isConstant: boolean
 | 
			
		||||
  constType: ConstantTypes
 | 
			
		||||
  /**
 | 
			
		||||
   * Indicates this is an identifier for a hoist vnode call and points to the
 | 
			
		||||
   * hoisted node.
 | 
			
		||||
@ -204,11 +216,6 @@ export interface SimpleExpressionNode extends Node {
 | 
			
		||||
   * the identifiers declared inside the function body.
 | 
			
		||||
   */
 | 
			
		||||
  identifiers?: string[]
 | 
			
		||||
  /**
 | 
			
		||||
   * some expressions (e.g. transformAssetUrls import identifiers) are constant,
 | 
			
		||||
   * but cannot be stringified because they must be first evaluated at runtime.
 | 
			
		||||
   */
 | 
			
		||||
  isRuntimeConstant?: boolean
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface InterpolationNode extends Node {
 | 
			
		||||
@ -611,14 +618,14 @@ export function createSimpleExpression(
 | 
			
		||||
  content: SimpleExpressionNode['content'],
 | 
			
		||||
  isStatic: SimpleExpressionNode['isStatic'],
 | 
			
		||||
  loc: SourceLocation = locStub,
 | 
			
		||||
  isConstant: boolean = false
 | 
			
		||||
  constType: ConstantTypes = ConstantTypes.NOT_CONSTANT
 | 
			
		||||
): SimpleExpressionNode {
 | 
			
		||||
  return {
 | 
			
		||||
    type: NodeTypes.SIMPLE_EXPRESSION,
 | 
			
		||||
    loc,
 | 
			
		||||
    isConstant,
 | 
			
		||||
    content,
 | 
			
		||||
    isStatic
 | 
			
		||||
    isStatic,
 | 
			
		||||
    constType: isStatic ? ConstantTypes.CAN_STRINGIFY : constType
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -22,7 +22,8 @@ import {
 | 
			
		||||
  TextNode,
 | 
			
		||||
  TemplateChildNode,
 | 
			
		||||
  InterpolationNode,
 | 
			
		||||
  createRoot
 | 
			
		||||
  createRoot,
 | 
			
		||||
  ConstantTypes
 | 
			
		||||
} from './ast'
 | 
			
		||||
 | 
			
		||||
type OptionalOptions = 'isNativeTag' | 'isBuiltInComponent'
 | 
			
		||||
@ -656,7 +657,9 @@ function parseAttribute(
 | 
			
		||||
        type: NodeTypes.SIMPLE_EXPRESSION,
 | 
			
		||||
        content,
 | 
			
		||||
        isStatic,
 | 
			
		||||
        isConstant: isStatic,
 | 
			
		||||
        constType: isStatic
 | 
			
		||||
          ? ConstantTypes.CAN_STRINGIFY
 | 
			
		||||
          : ConstantTypes.NOT_CONSTANT,
 | 
			
		||||
        loc
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
@ -677,8 +680,8 @@ function parseAttribute(
 | 
			
		||||
        content: value.content,
 | 
			
		||||
        isStatic: false,
 | 
			
		||||
        // Treat as non-constant by default. This can be potentially set to
 | 
			
		||||
        // true by `transformExpression` to make it eligible for hoisting.
 | 
			
		||||
        isConstant: false,
 | 
			
		||||
        // other values by `transformExpression` to make it eligible for hoisting.
 | 
			
		||||
        constType: ConstantTypes.NOT_CONSTANT,
 | 
			
		||||
        loc: value.loc
 | 
			
		||||
      },
 | 
			
		||||
      arg,
 | 
			
		||||
@ -785,7 +788,7 @@ function parseInterpolation(
 | 
			
		||||
      type: NodeTypes.SIMPLE_EXPRESSION,
 | 
			
		||||
      isStatic: false,
 | 
			
		||||
      // Set `isConstant` to false by default and will decide in transformExpression
 | 
			
		||||
      isConstant: false,
 | 
			
		||||
      constType: ConstantTypes.NOT_CONSTANT,
 | 
			
		||||
      content,
 | 
			
		||||
      loc: getSelection(context, innerStart, innerEnd)
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@ -15,7 +15,8 @@ import {
 | 
			
		||||
  CacheExpression,
 | 
			
		||||
  createCacheExpression,
 | 
			
		||||
  TemplateLiteral,
 | 
			
		||||
  createVNodeCall
 | 
			
		||||
  createVNodeCall,
 | 
			
		||||
  ConstantTypes
 | 
			
		||||
} from './ast'
 | 
			
		||||
import {
 | 
			
		||||
  isString,
 | 
			
		||||
@ -245,7 +246,7 @@ export function createTransformContext(
 | 
			
		||||
        `_hoisted_${context.hoists.length}`,
 | 
			
		||||
        false,
 | 
			
		||||
        exp.loc,
 | 
			
		||||
        true
 | 
			
		||||
        ConstantTypes.CAN_HOIST
 | 
			
		||||
      )
 | 
			
		||||
      identifier.hoisted = exp
 | 
			
		||||
      return identifier
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,5 @@
 | 
			
		||||
import {
 | 
			
		||||
  ConstantTypes,
 | 
			
		||||
  RootNode,
 | 
			
		||||
  NodeTypes,
 | 
			
		||||
  TemplateChildNode,
 | 
			
		||||
@ -37,16 +38,10 @@ export function isSingleElementRoot(
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const enum StaticType {
 | 
			
		||||
  NOT_STATIC = 0,
 | 
			
		||||
  FULL_STATIC,
 | 
			
		||||
  HAS_RUNTIME_CONSTANT
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function walk(
 | 
			
		||||
  node: ParentNode,
 | 
			
		||||
  context: TransformContext,
 | 
			
		||||
  resultCache: Map<TemplateChildNode, StaticType>,
 | 
			
		||||
  resultCache: Map<TemplateChildNode, ConstantTypes>,
 | 
			
		||||
  doNotHoistNode: boolean = false
 | 
			
		||||
) {
 | 
			
		||||
  let hasHoistedNode = false
 | 
			
		||||
@ -58,7 +53,7 @@ function walk(
 | 
			
		||||
  // @vue/compiler-dom), but doing it here allows us to perform only one full
 | 
			
		||||
  // walk of the AST and allow `stringifyStatic` to stop walking as soon as its
 | 
			
		||||
  // stringficiation threshold is met.
 | 
			
		||||
  let hasRuntimeConstant = false
 | 
			
		||||
  let canStringify = true
 | 
			
		||||
 | 
			
		||||
  const { children } = node
 | 
			
		||||
  for (let i = 0; i < children.length; i++) {
 | 
			
		||||
@ -68,20 +63,20 @@ function walk(
 | 
			
		||||
      child.type === NodeTypes.ELEMENT &&
 | 
			
		||||
      child.tagType === ElementTypes.ELEMENT
 | 
			
		||||
    ) {
 | 
			
		||||
      let staticType
 | 
			
		||||
      if (
 | 
			
		||||
        !doNotHoistNode &&
 | 
			
		||||
        (staticType = getStaticType(child, resultCache)) > 0
 | 
			
		||||
      ) {
 | 
			
		||||
        if (staticType === StaticType.HAS_RUNTIME_CONSTANT) {
 | 
			
		||||
          hasRuntimeConstant = true
 | 
			
		||||
      const constantType = doNotHoistNode
 | 
			
		||||
        ? ConstantTypes.NOT_CONSTANT
 | 
			
		||||
        : getConstantType(child, resultCache)
 | 
			
		||||
      if (constantType > ConstantTypes.NOT_CONSTANT) {
 | 
			
		||||
        if (constantType < ConstantTypes.CAN_STRINGIFY) {
 | 
			
		||||
          canStringify = false
 | 
			
		||||
        }
 | 
			
		||||
        // whole tree is static
 | 
			
		||||
        if (constantType >= ConstantTypes.CAN_HOIST) {
 | 
			
		||||
          ;(child.codegenNode as VNodeCall).patchFlag =
 | 
			
		||||
            PatchFlags.HOISTED + (__DEV__ ? ` /* HOISTED */` : ``)
 | 
			
		||||
          child.codegenNode = context.hoist(child.codegenNode!)
 | 
			
		||||
          hasHoistedNode = true
 | 
			
		||||
          continue
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
        // node may contain dynamic children, but its props may be eligible for
 | 
			
		||||
        // hoisting.
 | 
			
		||||
@ -92,7 +87,8 @@ function walk(
 | 
			
		||||
            (!flag ||
 | 
			
		||||
              flag === PatchFlags.NEED_PATCH ||
 | 
			
		||||
              flag === PatchFlags.TEXT) &&
 | 
			
		||||
            !hasNonHoistableProps(child)
 | 
			
		||||
            getGeneratedPropsConstantType(child, resultCache) >=
 | 
			
		||||
              ConstantTypes.CAN_HOIST
 | 
			
		||||
          ) {
 | 
			
		||||
            const props = getNodeProps(child)
 | 
			
		||||
            if (props) {
 | 
			
		||||
@ -102,15 +98,17 @@ function walk(
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    } else if (child.type === NodeTypes.TEXT_CALL) {
 | 
			
		||||
      const staticType = getStaticType(child.content, resultCache)
 | 
			
		||||
      if (staticType > 0) {
 | 
			
		||||
        if (staticType === StaticType.HAS_RUNTIME_CONSTANT) {
 | 
			
		||||
          hasRuntimeConstant = true
 | 
			
		||||
      const contentType = getConstantType(child.content, resultCache)
 | 
			
		||||
      if (contentType > 0) {
 | 
			
		||||
        if (contentType < ConstantTypes.CAN_STRINGIFY) {
 | 
			
		||||
          canStringify = false
 | 
			
		||||
        }
 | 
			
		||||
        if (contentType >= ConstantTypes.CAN_HOIST) {
 | 
			
		||||
          child.codegenNode = context.hoist(child.codegenNode)
 | 
			
		||||
          hasHoistedNode = true
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // walk further
 | 
			
		||||
    if (child.type === NodeTypes.ELEMENT) {
 | 
			
		||||
@ -131,19 +129,19 @@ function walk(
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!hasRuntimeConstant && hasHoistedNode && context.transformHoist) {
 | 
			
		||||
  if (canStringify && hasHoistedNode && context.transformHoist) {
 | 
			
		||||
    context.transformHoist(children, context, node)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function getStaticType(
 | 
			
		||||
export function getConstantType(
 | 
			
		||||
  node: TemplateChildNode | SimpleExpressionNode,
 | 
			
		||||
  resultCache: Map<TemplateChildNode, StaticType> = new Map()
 | 
			
		||||
): StaticType {
 | 
			
		||||
  resultCache: Map<TemplateChildNode, ConstantTypes> = new Map()
 | 
			
		||||
): ConstantTypes {
 | 
			
		||||
  switch (node.type) {
 | 
			
		||||
    case NodeTypes.ELEMENT:
 | 
			
		||||
      if (node.tagType !== ElementTypes.ELEMENT) {
 | 
			
		||||
        return StaticType.NOT_STATIC
 | 
			
		||||
        return ConstantTypes.NOT_CONSTANT
 | 
			
		||||
      }
 | 
			
		||||
      const cached = resultCache.get(node)
 | 
			
		||||
      if (cached !== undefined) {
 | 
			
		||||
@ -151,40 +149,64 @@ export function getStaticType(
 | 
			
		||||
      }
 | 
			
		||||
      const codegenNode = node.codegenNode!
 | 
			
		||||
      if (codegenNode.type !== NodeTypes.VNODE_CALL) {
 | 
			
		||||
        return StaticType.NOT_STATIC
 | 
			
		||||
        return ConstantTypes.NOT_CONSTANT
 | 
			
		||||
      }
 | 
			
		||||
      const flag = getPatchFlag(codegenNode)
 | 
			
		||||
      if (!flag && !hasNonHoistableProps(node)) {
 | 
			
		||||
        // element self is static. check its children.
 | 
			
		||||
        let returnType = StaticType.FULL_STATIC
 | 
			
		||||
      if (!flag) {
 | 
			
		||||
        let returnType = ConstantTypes.CAN_STRINGIFY
 | 
			
		||||
 | 
			
		||||
        // Element itself has no patch flag. However we still need to check:
 | 
			
		||||
 | 
			
		||||
        // 1. Even for a node with no patch flag, it is possible for it to contain
 | 
			
		||||
        // non-hoistable expressions that refers to scope variables, e.g. compiler
 | 
			
		||||
        // injected keys or cached event handlers. Therefore we need to always
 | 
			
		||||
        // check the codegenNode's props to be sure.
 | 
			
		||||
        const generatedPropsType = getGeneratedPropsConstantType(
 | 
			
		||||
          node,
 | 
			
		||||
          resultCache
 | 
			
		||||
        )
 | 
			
		||||
        if (generatedPropsType === ConstantTypes.NOT_CONSTANT) {
 | 
			
		||||
          resultCache.set(node, ConstantTypes.NOT_CONSTANT)
 | 
			
		||||
          return ConstantTypes.NOT_CONSTANT
 | 
			
		||||
        }
 | 
			
		||||
        if (generatedPropsType < returnType) {
 | 
			
		||||
          returnType = generatedPropsType
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 2. its children.
 | 
			
		||||
        for (let i = 0; i < node.children.length; i++) {
 | 
			
		||||
          const childType = getStaticType(node.children[i], resultCache)
 | 
			
		||||
          if (childType === StaticType.NOT_STATIC) {
 | 
			
		||||
            resultCache.set(node, StaticType.NOT_STATIC)
 | 
			
		||||
            return StaticType.NOT_STATIC
 | 
			
		||||
          } else if (childType === StaticType.HAS_RUNTIME_CONSTANT) {
 | 
			
		||||
            returnType = StaticType.HAS_RUNTIME_CONSTANT
 | 
			
		||||
          const childType = getConstantType(node.children[i], resultCache)
 | 
			
		||||
          if (childType === ConstantTypes.NOT_CONSTANT) {
 | 
			
		||||
            resultCache.set(node, ConstantTypes.NOT_CONSTANT)
 | 
			
		||||
            return ConstantTypes.NOT_CONSTANT
 | 
			
		||||
          }
 | 
			
		||||
          if (childType < returnType) {
 | 
			
		||||
            returnType = childType
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // check if any of the props contain runtime constants
 | 
			
		||||
        if (returnType !== StaticType.HAS_RUNTIME_CONSTANT) {
 | 
			
		||||
        // 3. if the type is not already CAN_SKIP_PATCH which is the lowest non-0
 | 
			
		||||
        // type, check if any of the props can cause the type to be lowered
 | 
			
		||||
        // we can skip can_patch because it's guaranteed by the absence of a
 | 
			
		||||
        // patchFlag.
 | 
			
		||||
        if (returnType > ConstantTypes.CAN_SKIP_PATCH) {
 | 
			
		||||
          for (let i = 0; i < node.props.length; i++) {
 | 
			
		||||
            const p = node.props[i]
 | 
			
		||||
            if (
 | 
			
		||||
              p.type === NodeTypes.DIRECTIVE &&
 | 
			
		||||
              p.name === 'bind' &&
 | 
			
		||||
              p.exp &&
 | 
			
		||||
              (p.exp.type === NodeTypes.COMPOUND_EXPRESSION ||
 | 
			
		||||
                p.exp.isRuntimeConstant)
 | 
			
		||||
            ) {
 | 
			
		||||
              returnType = StaticType.HAS_RUNTIME_CONSTANT
 | 
			
		||||
            if (p.type === NodeTypes.DIRECTIVE && p.name === 'bind' && p.exp) {
 | 
			
		||||
              const expType = getConstantType(p.exp, resultCache)
 | 
			
		||||
              if (expType === ConstantTypes.NOT_CONSTANT) {
 | 
			
		||||
                resultCache.set(node, ConstantTypes.NOT_CONSTANT)
 | 
			
		||||
                return ConstantTypes.NOT_CONSTANT
 | 
			
		||||
              }
 | 
			
		||||
              if (expType < returnType) {
 | 
			
		||||
                returnType = expType
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // only svg/foreignObject could be block here, however if they are
 | 
			
		||||
        // stati then they don't need to be blocks since there will be no
 | 
			
		||||
        // static then they don't need to be blocks since there will be no
 | 
			
		||||
        // nested updates.
 | 
			
		||||
        if (codegenNode.isBlock) {
 | 
			
		||||
          codegenNode.isBlock = false
 | 
			
		||||
@ -193,37 +215,33 @@ export function getStaticType(
 | 
			
		||||
        resultCache.set(node, returnType)
 | 
			
		||||
        return returnType
 | 
			
		||||
      } else {
 | 
			
		||||
        resultCache.set(node, StaticType.NOT_STATIC)
 | 
			
		||||
        return StaticType.NOT_STATIC
 | 
			
		||||
        resultCache.set(node, ConstantTypes.NOT_CONSTANT)
 | 
			
		||||
        return ConstantTypes.NOT_CONSTANT
 | 
			
		||||
      }
 | 
			
		||||
    case NodeTypes.TEXT:
 | 
			
		||||
    case NodeTypes.COMMENT:
 | 
			
		||||
      return StaticType.FULL_STATIC
 | 
			
		||||
      return ConstantTypes.CAN_STRINGIFY
 | 
			
		||||
    case NodeTypes.IF:
 | 
			
		||||
    case NodeTypes.FOR:
 | 
			
		||||
    case NodeTypes.IF_BRANCH:
 | 
			
		||||
      return StaticType.NOT_STATIC
 | 
			
		||||
      return ConstantTypes.NOT_CONSTANT
 | 
			
		||||
    case NodeTypes.INTERPOLATION:
 | 
			
		||||
    case NodeTypes.TEXT_CALL:
 | 
			
		||||
      return getStaticType(node.content, resultCache)
 | 
			
		||||
      return getConstantType(node.content, resultCache)
 | 
			
		||||
    case NodeTypes.SIMPLE_EXPRESSION:
 | 
			
		||||
      return node.isRuntimeConstant
 | 
			
		||||
        ? StaticType.HAS_RUNTIME_CONSTANT
 | 
			
		||||
        : node.isConstant
 | 
			
		||||
          ? StaticType.FULL_STATIC
 | 
			
		||||
          : StaticType.NOT_STATIC
 | 
			
		||||
      return node.constType
 | 
			
		||||
    case NodeTypes.COMPOUND_EXPRESSION:
 | 
			
		||||
      let returnType = StaticType.FULL_STATIC
 | 
			
		||||
      let returnType = ConstantTypes.CAN_STRINGIFY
 | 
			
		||||
      for (let i = 0; i < node.children.length; i++) {
 | 
			
		||||
        const child = node.children[i]
 | 
			
		||||
        if (isString(child) || isSymbol(child)) {
 | 
			
		||||
          continue
 | 
			
		||||
        }
 | 
			
		||||
        const childType = getStaticType(child, resultCache)
 | 
			
		||||
        if (childType === StaticType.NOT_STATIC) {
 | 
			
		||||
          return StaticType.NOT_STATIC
 | 
			
		||||
        } else if (childType === StaticType.HAS_RUNTIME_CONSTANT) {
 | 
			
		||||
          returnType = StaticType.HAS_RUNTIME_CONSTANT
 | 
			
		||||
        const childType = getConstantType(child, resultCache)
 | 
			
		||||
        if (childType === ConstantTypes.NOT_CONSTANT) {
 | 
			
		||||
          return ConstantTypes.NOT_CONSTANT
 | 
			
		||||
        } else if (childType < returnType) {
 | 
			
		||||
          returnType = childType
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      return returnType
 | 
			
		||||
@ -232,33 +250,40 @@ export function getStaticType(
 | 
			
		||||
        const exhaustiveCheck: never = node
 | 
			
		||||
        exhaustiveCheck
 | 
			
		||||
      }
 | 
			
		||||
      return StaticType.NOT_STATIC
 | 
			
		||||
      return ConstantTypes.NOT_CONSTANT
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Even for a node with no patch flag, it is possible for it to contain
 | 
			
		||||
 * non-hoistable expressions that refers to scope variables, e.g. compiler
 | 
			
		||||
 * injected keys or cached event handlers. Therefore we need to always check the
 | 
			
		||||
 * codegenNode's props to be sure.
 | 
			
		||||
 */
 | 
			
		||||
function hasNonHoistableProps(node: PlainElementNode): boolean {
 | 
			
		||||
function getGeneratedPropsConstantType(
 | 
			
		||||
  node: PlainElementNode,
 | 
			
		||||
  resultCache: Map<TemplateChildNode, ConstantTypes>
 | 
			
		||||
): ConstantTypes {
 | 
			
		||||
  let returnType = ConstantTypes.CAN_STRINGIFY
 | 
			
		||||
  const props = getNodeProps(node)
 | 
			
		||||
  if (props && props.type === NodeTypes.JS_OBJECT_EXPRESSION) {
 | 
			
		||||
    const { properties } = props
 | 
			
		||||
    for (let i = 0; i < properties.length; i++) {
 | 
			
		||||
      const { key, value } = properties[i]
 | 
			
		||||
      if (
 | 
			
		||||
        key.type !== NodeTypes.SIMPLE_EXPRESSION ||
 | 
			
		||||
        !key.isStatic ||
 | 
			
		||||
        (value.type !== NodeTypes.SIMPLE_EXPRESSION ||
 | 
			
		||||
          (!value.isStatic && !value.isConstant))
 | 
			
		||||
      ) {
 | 
			
		||||
        return true
 | 
			
		||||
      const keyType = getConstantType(key, resultCache)
 | 
			
		||||
      if (keyType === ConstantTypes.NOT_CONSTANT) {
 | 
			
		||||
        return keyType
 | 
			
		||||
      }
 | 
			
		||||
      if (keyType < returnType) {
 | 
			
		||||
        returnType = keyType
 | 
			
		||||
      }
 | 
			
		||||
      if (value.type !== NodeTypes.SIMPLE_EXPRESSION) {
 | 
			
		||||
        return ConstantTypes.NOT_CONSTANT
 | 
			
		||||
      }
 | 
			
		||||
      const valueType = getConstantType(value, resultCache)
 | 
			
		||||
      if (valueType === ConstantTypes.NOT_CONSTANT) {
 | 
			
		||||
        return valueType
 | 
			
		||||
      }
 | 
			
		||||
      if (valueType < returnType) {
 | 
			
		||||
        returnType = valueType
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return false
 | 
			
		||||
  return returnType
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getNodeProps(node: PlainElementNode) {
 | 
			
		||||
 | 
			
		||||
@ -18,7 +18,8 @@ import {
 | 
			
		||||
  VNodeCall,
 | 
			
		||||
  TemplateTextChildNode,
 | 
			
		||||
  DirectiveArguments,
 | 
			
		||||
  createVNodeCall
 | 
			
		||||
  createVNodeCall,
 | 
			
		||||
  ConstantTypes
 | 
			
		||||
} from '../ast'
 | 
			
		||||
import {
 | 
			
		||||
  PatchFlags,
 | 
			
		||||
@ -53,7 +54,7 @@ import {
 | 
			
		||||
  isStaticExp
 | 
			
		||||
} from '../utils'
 | 
			
		||||
import { buildSlots } from './vSlot'
 | 
			
		||||
import { getStaticType } from './hoistStatic'
 | 
			
		||||
import { getConstantType } from './hoistStatic'
 | 
			
		||||
import { BindingTypes } from '../options'
 | 
			
		||||
 | 
			
		||||
// some directive transforms (e.g. v-model) may return a symbol for runtime
 | 
			
		||||
@ -166,7 +167,10 @@ export const transformElement: NodeTransform = (node, context) => {
 | 
			
		||||
        const hasDynamicTextChild =
 | 
			
		||||
          type === NodeTypes.INTERPOLATION ||
 | 
			
		||||
          type === NodeTypes.COMPOUND_EXPRESSION
 | 
			
		||||
        if (hasDynamicTextChild && !getStaticType(child)) {
 | 
			
		||||
        if (
 | 
			
		||||
          hasDynamicTextChild &&
 | 
			
		||||
          getConstantType(child) === ConstantTypes.NOT_CONSTANT
 | 
			
		||||
        ) {
 | 
			
		||||
          patchFlag |= PatchFlags.TEXT
 | 
			
		||||
        }
 | 
			
		||||
        // pass directly if the only child is a text node
 | 
			
		||||
@ -343,7 +347,7 @@ export function buildProps(
 | 
			
		||||
        value.type === NodeTypes.JS_CACHE_EXPRESSION ||
 | 
			
		||||
        ((value.type === NodeTypes.SIMPLE_EXPRESSION ||
 | 
			
		||||
          value.type === NodeTypes.COMPOUND_EXPRESSION) &&
 | 
			
		||||
          getStaticType(value) > 0)
 | 
			
		||||
          getConstantType(value) > 0)
 | 
			
		||||
      ) {
 | 
			
		||||
        // skip if the prop is a cached handler or has constant value
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
@ -14,7 +14,8 @@ import {
 | 
			
		||||
  ExpressionNode,
 | 
			
		||||
  SimpleExpressionNode,
 | 
			
		||||
  CompoundExpressionNode,
 | 
			
		||||
  createCompoundExpression
 | 
			
		||||
  createCompoundExpression,
 | 
			
		||||
  ConstantTypes
 | 
			
		||||
} from '../ast'
 | 
			
		||||
import { advancePositionWithClone, isSimpleIdentifier } from '../utils'
 | 
			
		||||
import {
 | 
			
		||||
@ -190,25 +191,26 @@ export function processExpression(
 | 
			
		||||
 | 
			
		||||
  // fast path if expression is a simple identifier.
 | 
			
		||||
  const rawExp = node.content
 | 
			
		||||
  // bail on parens to prevent any possible function invocations.
 | 
			
		||||
  const bailConstant = rawExp.indexOf(`(`) > -1
 | 
			
		||||
  // bail constant on parens (function invocation) and dot (member access)
 | 
			
		||||
  const bailConstant = rawExp.indexOf(`(`) > -1 || rawExp.indexOf('.') > 0
 | 
			
		||||
 | 
			
		||||
  if (isSimpleIdentifier(rawExp)) {
 | 
			
		||||
    // const bindings exposed from setup - we know they never change
 | 
			
		||||
    // marking it as runtime constant will prevent it from being listed as
 | 
			
		||||
    // a dynamic prop.
 | 
			
		||||
    const isScopeVarReference = context.identifiers[rawExp]
 | 
			
		||||
    const isAllowedGlobal = isGloballyWhitelisted(rawExp)
 | 
			
		||||
    const isLiteral = isLiteralWhitelisted(rawExp)
 | 
			
		||||
    if (!asParams && !isScopeVarReference && !isAllowedGlobal && !isLiteral) {
 | 
			
		||||
      // const bindings exposed from setup can be skipped for patching but
 | 
			
		||||
      // cannot be hoisted to module scope
 | 
			
		||||
      if (bindingMetadata[node.content] === BindingTypes.SETUP_CONST) {
 | 
			
		||||
      node.isRuntimeConstant = true
 | 
			
		||||
        node.constType = ConstantTypes.CAN_SKIP_PATCH
 | 
			
		||||
      }
 | 
			
		||||
    if (
 | 
			
		||||
      !asParams &&
 | 
			
		||||
      !context.identifiers[rawExp] &&
 | 
			
		||||
      !isGloballyWhitelisted(rawExp) &&
 | 
			
		||||
      !isLiteralWhitelisted(rawExp)
 | 
			
		||||
    ) {
 | 
			
		||||
      node.content = rewriteIdentifier(rawExp)
 | 
			
		||||
    } else if (!context.identifiers[rawExp] && !bailConstant) {
 | 
			
		||||
      // mark node constant for hoisting unless it's referring a scope variable
 | 
			
		||||
      node.isConstant = true
 | 
			
		||||
    } else if (!isScopeVarReference) {
 | 
			
		||||
      if (isLiteral) {
 | 
			
		||||
        node.constType = ConstantTypes.CAN_STRINGIFY
 | 
			
		||||
      } else {
 | 
			
		||||
        node.constType = ConstantTypes.CAN_HOIST
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return node
 | 
			
		||||
  }
 | 
			
		||||
@ -342,7 +344,7 @@ export function processExpression(
 | 
			
		||||
          start: advancePositionWithClone(node.loc.start, source, start),
 | 
			
		||||
          end: advancePositionWithClone(node.loc.start, source, end)
 | 
			
		||||
        },
 | 
			
		||||
        id.isConstant /* isConstant */
 | 
			
		||||
        id.isConstant ? ConstantTypes.CAN_STRINGIFY : ConstantTypes.NOT_CONSTANT
 | 
			
		||||
      )
 | 
			
		||||
    )
 | 
			
		||||
    if (i === ids.length - 1 && end < rawExp.length) {
 | 
			
		||||
@ -355,7 +357,9 @@ export function processExpression(
 | 
			
		||||
    ret = createCompoundExpression(children, node.loc)
 | 
			
		||||
  } else {
 | 
			
		||||
    ret = node
 | 
			
		||||
    ret.isConstant = !bailConstant
 | 
			
		||||
    ret.constType = bailConstant
 | 
			
		||||
      ? ConstantTypes.NOT_CONSTANT
 | 
			
		||||
      : ConstantTypes.CAN_STRINGIFY
 | 
			
		||||
  }
 | 
			
		||||
  ret.identifiers = Object.keys(knownIds)
 | 
			
		||||
  return ret
 | 
			
		||||
 | 
			
		||||
@ -4,11 +4,13 @@ import {
 | 
			
		||||
  CompoundExpressionNode,
 | 
			
		||||
  createCallExpression,
 | 
			
		||||
  CallExpression,
 | 
			
		||||
  ElementTypes
 | 
			
		||||
  ElementTypes,
 | 
			
		||||
  ConstantTypes
 | 
			
		||||
} from '../ast'
 | 
			
		||||
import { isText } from '../utils'
 | 
			
		||||
import { CREATE_TEXT } from '../runtimeHelpers'
 | 
			
		||||
import { PatchFlags, PatchFlagNames } from '@vue/shared'
 | 
			
		||||
import { getConstantType } from './hoistStatic'
 | 
			
		||||
 | 
			
		||||
// Merge adjacent text nodes and expressions into a single expression
 | 
			
		||||
// e.g. <div>abc {{ d }} {{ e }}</div> should have a single expression node as child.
 | 
			
		||||
@ -78,7 +80,10 @@ export const transformText: NodeTransform = (node, context) => {
 | 
			
		||||
            callArgs.push(child)
 | 
			
		||||
          }
 | 
			
		||||
          // mark dynamic text with flag so it gets patched inside a block
 | 
			
		||||
          if (!context.ssr && child.type !== NodeTypes.TEXT) {
 | 
			
		||||
          if (
 | 
			
		||||
            !context.ssr &&
 | 
			
		||||
            getConstantType(child) === ConstantTypes.NOT_CONSTANT
 | 
			
		||||
          ) {
 | 
			
		||||
            callArgs.push(
 | 
			
		||||
              `${PatchFlags.TEXT} /* ${PatchFlagNames[PatchFlags.TEXT]} */`
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
@ -77,7 +77,7 @@ export const transformFor = createStructuralDirectiveTransform(
 | 
			
		||||
 | 
			
		||||
      const isStableFragment =
 | 
			
		||||
        forNode.source.type === NodeTypes.SIMPLE_EXPRESSION &&
 | 
			
		||||
        forNode.source.isConstant
 | 
			
		||||
        forNode.source.constType > 0
 | 
			
		||||
      const fragmentFlag = isStableFragment
 | 
			
		||||
        ? PatchFlags.STABLE_FRAGMENT
 | 
			
		||||
        : keyProp
 | 
			
		||||
 | 
			
		||||
@ -21,7 +21,8 @@ import {
 | 
			
		||||
  createVNodeCall,
 | 
			
		||||
  AttributeNode,
 | 
			
		||||
  locStub,
 | 
			
		||||
  CacheExpression
 | 
			
		||||
  CacheExpression,
 | 
			
		||||
  ConstantTypes
 | 
			
		||||
} from '../ast'
 | 
			
		||||
import { createCompilerError, ErrorCodes } from '../errors'
 | 
			
		||||
import { processExpression } from './transformExpression'
 | 
			
		||||
@ -227,7 +228,12 @@ function createChildrenCodegenNode(
 | 
			
		||||
  const { helper } = context
 | 
			
		||||
  const keyProperty = createObjectProperty(
 | 
			
		||||
    `key`,
 | 
			
		||||
    createSimpleExpression(`${keyIndex}`, false, locStub, true)
 | 
			
		||||
    createSimpleExpression(
 | 
			
		||||
      `${keyIndex}`,
 | 
			
		||||
      false,
 | 
			
		||||
      locStub,
 | 
			
		||||
      ConstantTypes.CAN_HOIST
 | 
			
		||||
    )
 | 
			
		||||
  )
 | 
			
		||||
  const { children } = branch
 | 
			
		||||
  const firstChild = children[0]
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,8 @@ import {
 | 
			
		||||
  NodeTypes,
 | 
			
		||||
  Property,
 | 
			
		||||
  ElementTypes,
 | 
			
		||||
  ExpressionNode
 | 
			
		||||
  ExpressionNode,
 | 
			
		||||
  ConstantTypes
 | 
			
		||||
} from '../ast'
 | 
			
		||||
import { createCompilerError, ErrorCodes } from '../errors'
 | 
			
		||||
import {
 | 
			
		||||
@ -125,7 +126,12 @@ export const transformModel: DirectiveTransform = (dir, node, context) => {
 | 
			
		||||
    props.push(
 | 
			
		||||
      createObjectProperty(
 | 
			
		||||
        modifiersKey,
 | 
			
		||||
        createSimpleExpression(`{ ${modifiers} }`, false, dir.loc, true)
 | 
			
		||||
        createSimpleExpression(
 | 
			
		||||
          `{ ${modifiers} }`,
 | 
			
		||||
          false,
 | 
			
		||||
          dir.loc,
 | 
			
		||||
          ConstantTypes.CAN_HOIST
 | 
			
		||||
        )
 | 
			
		||||
      )
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -87,7 +87,7 @@ export const transformOn: DirectiveTransform = (
 | 
			
		||||
        context.cacheHandlers &&
 | 
			
		||||
        // runtime constants don't need to be cached
 | 
			
		||||
        // (this is analyzed by compileScript in SFC <script setup>)
 | 
			
		||||
        !(exp.type === NodeTypes.SIMPLE_EXPRESSION && exp.isRuntimeConstant) &&
 | 
			
		||||
        !(exp.type === NodeTypes.SIMPLE_EXPRESSION && exp.constType > 0) &&
 | 
			
		||||
        // #1541 bail if this is a member exp handler passed to a component -
 | 
			
		||||
        // we need to use the original function to preserve arity,
 | 
			
		||||
        // e.g. <transition> relies on checking cb.length to determine
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,8 @@ import {
 | 
			
		||||
  ErrorCodes,
 | 
			
		||||
  ElementTypes,
 | 
			
		||||
  InterpolationNode,
 | 
			
		||||
  AttributeNode
 | 
			
		||||
  AttributeNode,
 | 
			
		||||
  ConstantTypes
 | 
			
		||||
} from '@vue/compiler-core'
 | 
			
		||||
import { parserOptions, DOMNamespaces } from '../src/parserOptions'
 | 
			
		||||
 | 
			
		||||
@ -253,7 +254,7 @@ describe('DOM parser', () => {
 | 
			
		||||
          type: NodeTypes.SIMPLE_EXPRESSION,
 | 
			
		||||
          content: `a < b`,
 | 
			
		||||
          isStatic: false,
 | 
			
		||||
          isConstant: false,
 | 
			
		||||
          constType: ConstantTypes.NOT_CONSTANT,
 | 
			
		||||
          loc: {
 | 
			
		||||
            start: { offset: 8, line: 1, column: 9 },
 | 
			
		||||
            end: { offset: 16, line: 1, column: 17 },
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,8 @@ import {
 | 
			
		||||
  compile,
 | 
			
		||||
  NodeTypes,
 | 
			
		||||
  CREATE_STATIC,
 | 
			
		||||
  createSimpleExpression
 | 
			
		||||
  createSimpleExpression,
 | 
			
		||||
  ConstantTypes
 | 
			
		||||
} from '../../src'
 | 
			
		||||
import {
 | 
			
		||||
  stringifyStatic,
 | 
			
		||||
@ -176,9 +177,8 @@ describe('stringify static html', () => {
 | 
			
		||||
                '_imports_0_',
 | 
			
		||||
                false,
 | 
			
		||||
                node.loc,
 | 
			
		||||
                true
 | 
			
		||||
                ConstantTypes.CAN_HOIST
 | 
			
		||||
              )
 | 
			
		||||
              exp.isRuntimeConstant = true
 | 
			
		||||
              node.props[0] = {
 | 
			
		||||
                type: NodeTypes.DIRECTIVE,
 | 
			
		||||
                name: 'bind',
 | 
			
		||||
 | 
			
		||||
@ -3,7 +3,8 @@ import {
 | 
			
		||||
  NodeTypes,
 | 
			
		||||
  createSimpleExpression,
 | 
			
		||||
  SimpleExpressionNode,
 | 
			
		||||
  SourceLocation
 | 
			
		||||
  SourceLocation,
 | 
			
		||||
  ConstantTypes
 | 
			
		||||
} from '@vue/compiler-core'
 | 
			
		||||
import { parseStringStyle } from '@vue/shared'
 | 
			
		||||
 | 
			
		||||
@ -36,5 +37,10 @@ const parseInlineCSS = (
 | 
			
		||||
  loc: SourceLocation
 | 
			
		||||
): SimpleExpressionNode => {
 | 
			
		||||
  const normalized = parseStringStyle(cssText)
 | 
			
		||||
  return createSimpleExpression(JSON.stringify(normalized), false, loc, true)
 | 
			
		||||
  return createSimpleExpression(
 | 
			
		||||
    JSON.stringify(normalized),
 | 
			
		||||
    false,
 | 
			
		||||
    loc,
 | 
			
		||||
    ConstantTypes.CAN_STRINGIFY
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,6 @@
 | 
			
		||||
import path from 'path'
 | 
			
		||||
import {
 | 
			
		||||
  ConstantTypes,
 | 
			
		||||
  createSimpleExpression,
 | 
			
		||||
  ExpressionNode,
 | 
			
		||||
  NodeTransform,
 | 
			
		||||
@ -159,19 +160,26 @@ function getImportsExpressionExp(
 | 
			
		||||
      return existing.exp as ExpressionNode
 | 
			
		||||
    }
 | 
			
		||||
    const name = `_imports_${importsArray.length}`
 | 
			
		||||
    const exp = createSimpleExpression(name, false, loc, true)
 | 
			
		||||
    exp.isRuntimeConstant = true
 | 
			
		||||
    const exp = createSimpleExpression(
 | 
			
		||||
      name,
 | 
			
		||||
      false,
 | 
			
		||||
      loc,
 | 
			
		||||
      ConstantTypes.CAN_HOIST
 | 
			
		||||
    )
 | 
			
		||||
    context.imports.add({ exp, path })
 | 
			
		||||
    if (hash && path) {
 | 
			
		||||
      const ret = context.hoist(
 | 
			
		||||
        createSimpleExpression(`${name} + '${hash}'`, false, loc, true)
 | 
			
		||||
      return context.hoist(
 | 
			
		||||
        createSimpleExpression(
 | 
			
		||||
          `${name} + '${hash}'`,
 | 
			
		||||
          false,
 | 
			
		||||
          loc,
 | 
			
		||||
          ConstantTypes.CAN_HOIST
 | 
			
		||||
        )
 | 
			
		||||
      )
 | 
			
		||||
      ret.isRuntimeConstant = true
 | 
			
		||||
      return ret
 | 
			
		||||
    } else {
 | 
			
		||||
      return exp
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    return createSimpleExpression(`''`, false, loc, true)
 | 
			
		||||
    return createSimpleExpression(`''`, false, loc, ConstantTypes.CAN_HOIST)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,6 @@
 | 
			
		||||
import path from 'path'
 | 
			
		||||
import {
 | 
			
		||||
  ConstantTypes,
 | 
			
		||||
  createCompoundExpression,
 | 
			
		||||
  createSimpleExpression,
 | 
			
		||||
  NodeTransform,
 | 
			
		||||
@ -107,14 +108,14 @@ export const transformSrcset: NodeTransform = (
 | 
			
		||||
                    `_imports_${existingImportsIndex}`,
 | 
			
		||||
                    false,
 | 
			
		||||
                    attr.loc,
 | 
			
		||||
                    true
 | 
			
		||||
                    ConstantTypes.CAN_HOIST
 | 
			
		||||
                  )
 | 
			
		||||
                } else {
 | 
			
		||||
                  exp = createSimpleExpression(
 | 
			
		||||
                    `_imports_${importsArray.length}`,
 | 
			
		||||
                    false,
 | 
			
		||||
                    attr.loc,
 | 
			
		||||
                    true
 | 
			
		||||
                    ConstantTypes.CAN_HOIST
 | 
			
		||||
                  )
 | 
			
		||||
                  context.imports.add({ exp, path })
 | 
			
		||||
                }
 | 
			
		||||
@ -125,7 +126,7 @@ export const transformSrcset: NodeTransform = (
 | 
			
		||||
                `"${url}"`,
 | 
			
		||||
                false,
 | 
			
		||||
                attr.loc,
 | 
			
		||||
                true
 | 
			
		||||
                ConstantTypes.CAN_HOIST
 | 
			
		||||
              )
 | 
			
		||||
              compoundExpression.children.push(exp)
 | 
			
		||||
            }
 | 
			
		||||
@ -140,7 +141,7 @@ export const transformSrcset: NodeTransform = (
 | 
			
		||||
          })
 | 
			
		||||
 | 
			
		||||
          const hoisted = context.hoist(compoundExpression)
 | 
			
		||||
          hoisted.isRuntimeConstant = true
 | 
			
		||||
          hoisted.constType = ConstantTypes.CAN_HOIST
 | 
			
		||||
 | 
			
		||||
          node.props[index] = {
 | 
			
		||||
            type: NodeTypes.DIRECTIVE,
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user