refactor(compiler): better constant hoist/stringify checks

This commit is contained in:
Evan You 2020-11-20 19:26:07 -05:00
parent acba86ef45
commit 90bdf59f4c
22 changed files with 291 additions and 204 deletions

View File

@ -3322,8 +3322,8 @@ Object {
"children": Array [ "children": Array [
Object { Object {
"content": Object { "content": Object {
"constType": 0,
"content": "a < b", "content": "a < b",
"isConstant": false,
"isStatic": false, "isStatic": false,
"loc": Object { "loc": Object {
"end": Object { "end": Object {
@ -6084,8 +6084,8 @@ Object {
"children": Array [ "children": Array [
Object { Object {
"content": Object { "content": Object {
"constType": 0,
"content": "'</div>'", "content": "'</div>'",
"isConstant": false,
"isStatic": false, "isStatic": false,
"loc": Object { "loc": Object {
"end": Object { "end": Object {
@ -6259,8 +6259,8 @@ Object {
"props": Array [ "props": Array [
Object { Object {
"arg": Object { "arg": Object {
"constType": 0,
"content": "se", "content": "se",
"isConstant": false,
"isStatic": false, "isStatic": false,
"loc": Object { "loc": Object {
"end": Object { "end": Object {
@ -6593,8 +6593,8 @@ Object {
"children": Array [ "children": Array [
Object { Object {
"content": Object { "content": Object {
"constType": 0,
"content": "", "content": "",
"isConstant": false,
"isStatic": false, "isStatic": false,
"loc": Object { "loc": Object {
"end": Object { "end": Object {
@ -6758,8 +6758,8 @@ Object {
"props": Array [ "props": Array [
Object { Object {
"arg": Object { "arg": Object {
"constType": 3,
"content": "class", "content": "class",
"isConstant": true,
"isStatic": true, "isStatic": true,
"loc": Object { "loc": Object {
"end": Object { "end": Object {
@ -6777,8 +6777,8 @@ Object {
"type": 4, "type": 4,
}, },
"exp": Object { "exp": Object {
"constType": 0,
"content": "{ some: condition }", "content": "{ some: condition }",
"isConstant": false,
"isStatic": false, "isStatic": false,
"loc": Object { "loc": Object {
"end": Object { "end": Object {
@ -6838,8 +6838,8 @@ Object {
"props": Array [ "props": Array [
Object { Object {
"arg": Object { "arg": Object {
"constType": 3,
"content": "style", "content": "style",
"isConstant": true,
"isStatic": true, "isStatic": true,
"loc": Object { "loc": Object {
"end": Object { "end": Object {
@ -6857,8 +6857,8 @@ Object {
"type": 4, "type": 4,
}, },
"exp": Object { "exp": Object {
"constType": 0,
"content": "{ color: 'red' }", "content": "{ color: 'red' }",
"isConstant": false,
"isStatic": false, "isStatic": false,
"loc": Object { "loc": Object {
"end": Object { "end": Object {
@ -6950,8 +6950,8 @@ Object {
"props": Array [ "props": Array [
Object { Object {
"arg": Object { "arg": Object {
"constType": 3,
"content": "style", "content": "style",
"isConstant": true,
"isStatic": true, "isStatic": true,
"loc": Object { "loc": Object {
"end": Object { "end": Object {
@ -6969,8 +6969,8 @@ Object {
"type": 4, "type": 4,
}, },
"exp": Object { "exp": Object {
"constType": 0,
"content": "{ color: 'red' }", "content": "{ color: 'red' }",
"isConstant": false,
"isStatic": false, "isStatic": false,
"loc": Object { "loc": Object {
"end": Object { "end": Object {
@ -7049,8 +7049,8 @@ Object {
"props": Array [ "props": Array [
Object { Object {
"arg": Object { "arg": Object {
"constType": 3,
"content": "class", "content": "class",
"isConstant": true,
"isStatic": true, "isStatic": true,
"loc": Object { "loc": Object {
"end": Object { "end": Object {
@ -7068,8 +7068,8 @@ Object {
"type": 4, "type": 4,
}, },
"exp": Object { "exp": Object {
"constType": 0,
"content": "{ some: condition }", "content": "{ some: condition }",
"isConstant": false,
"isStatic": false, "isStatic": false,
"loc": Object { "loc": Object {
"end": Object { "end": Object {

View File

@ -20,7 +20,8 @@ import {
IfConditionalExpression, IfConditionalExpression,
createVNodeCall, createVNodeCall,
VNodeCall, VNodeCall,
DirectiveArguments DirectiveArguments,
ConstantTypes
} from '../src' } from '../src'
import { import {
CREATE_VNODE, CREATE_VNODE,
@ -304,7 +305,12 @@ describe('compiler: codegen', () => {
codegenNode: { codegenNode: {
type: NodeTypes.FOR, type: NodeTypes.FOR,
loc: locStub, loc: locStub,
source: createSimpleExpression('1 + 2', false, locStub, true), source: createSimpleExpression(
'1 + 2',
false,
locStub,
ConstantTypes.CAN_STRINGIFY
),
valueAlias: undefined, valueAlias: undefined,
keyAlias: undefined, keyAlias: undefined,
objectIndexAlias: undefined, objectIndexAlias: undefined,

View File

@ -9,7 +9,8 @@ import {
NodeTypes, NodeTypes,
Position, Position,
TextNode, TextNode,
InterpolationNode InterpolationNode,
ConstantTypes
} from '../src/ast' } from '../src/ast'
describe('compiler: parse', () => { describe('compiler: parse', () => {
@ -177,7 +178,7 @@ describe('compiler: parse', () => {
type: NodeTypes.SIMPLE_EXPRESSION, type: NodeTypes.SIMPLE_EXPRESSION,
content: `message`, content: `message`,
isStatic: false, isStatic: false,
isConstant: false, constType: ConstantTypes.NOT_CONSTANT,
loc: { loc: {
start: { offset: 2, line: 1, column: 3 }, start: { offset: 2, line: 1, column: 3 },
end: { offset: 9, line: 1, column: 10 }, end: { offset: 9, line: 1, column: 10 },
@ -202,7 +203,7 @@ describe('compiler: parse', () => {
type: NodeTypes.SIMPLE_EXPRESSION, type: NodeTypes.SIMPLE_EXPRESSION,
content: `a<b`, content: `a<b`,
isStatic: false, isStatic: false,
isConstant: false, constType: ConstantTypes.NOT_CONSTANT,
loc: { loc: {
start: { offset: 3, line: 1, column: 4 }, start: { offset: 3, line: 1, column: 4 },
end: { offset: 6, line: 1, column: 7 }, end: { offset: 6, line: 1, column: 7 },
@ -228,7 +229,7 @@ describe('compiler: parse', () => {
type: NodeTypes.SIMPLE_EXPRESSION, type: NodeTypes.SIMPLE_EXPRESSION,
content: `a<b`, content: `a<b`,
isStatic: false, isStatic: false,
isConstant: false, constType: ConstantTypes.NOT_CONSTANT,
loc: { loc: {
start: { offset: 3, line: 1, column: 4 }, start: { offset: 3, line: 1, column: 4 },
end: { offset: 6, line: 1, column: 7 }, end: { offset: 6, line: 1, column: 7 },
@ -247,7 +248,7 @@ describe('compiler: parse', () => {
content: { content: {
type: NodeTypes.SIMPLE_EXPRESSION, type: NodeTypes.SIMPLE_EXPRESSION,
isStatic: false, isStatic: false,
isConstant: false, constType: ConstantTypes.NOT_CONSTANT,
content: 'c>d', content: 'c>d',
loc: { loc: {
start: { offset: 12, line: 1, column: 13 }, start: { offset: 12, line: 1, column: 13 },
@ -273,8 +274,8 @@ describe('compiler: parse', () => {
content: { content: {
type: NodeTypes.SIMPLE_EXPRESSION, type: NodeTypes.SIMPLE_EXPRESSION,
isStatic: false, isStatic: false,
// The `isConstant` is the default value and will be determined in `transformExpression`. // The `constType` is the default value and will be determined in `transformExpression`.
isConstant: false, constType: ConstantTypes.NOT_CONSTANT,
content: '"</div>"', content: '"</div>"',
loc: { loc: {
start: { offset: 8, line: 1, column: 9 }, start: { offset: 8, line: 1, column: 9 },
@ -303,7 +304,7 @@ describe('compiler: parse', () => {
type: NodeTypes.SIMPLE_EXPRESSION, type: NodeTypes.SIMPLE_EXPRESSION,
content: `msg`, content: `msg`,
isStatic: false, isStatic: false,
isConstant: false, constType: ConstantTypes.NOT_CONSTANT,
loc: { loc: {
start: { offset: 4, line: 1, column: 5 }, start: { offset: 4, line: 1, column: 5 },
end: { offset: 7, line: 1, column: 8 }, end: { offset: 7, line: 1, column: 8 },
@ -1028,7 +1029,7 @@ describe('compiler: parse', () => {
type: NodeTypes.SIMPLE_EXPRESSION, type: NodeTypes.SIMPLE_EXPRESSION,
content: 'a', content: 'a',
isStatic: false, isStatic: false,
isConstant: false, constType: ConstantTypes.NOT_CONSTANT,
loc: { loc: {
start: { offset: 11, line: 1, column: 12 }, start: { offset: 11, line: 1, column: 12 },
end: { offset: 12, line: 1, column: 13 }, end: { offset: 12, line: 1, column: 13 },
@ -1054,7 +1055,7 @@ describe('compiler: parse', () => {
type: NodeTypes.SIMPLE_EXPRESSION, type: NodeTypes.SIMPLE_EXPRESSION,
content: 'click', content: 'click',
isStatic: true, isStatic: true,
isConstant: true, constType: ConstantTypes.CAN_STRINGIFY,
loc: { loc: {
source: 'click', source: 'click',
@ -1091,7 +1092,7 @@ describe('compiler: parse', () => {
type: NodeTypes.SIMPLE_EXPRESSION, type: NodeTypes.SIMPLE_EXPRESSION,
content: 'event', content: 'event',
isStatic: false, isStatic: false,
isConstant: false, constType: ConstantTypes.NOT_CONSTANT,
loc: { loc: {
source: '[event]', source: '[event]',
@ -1164,7 +1165,7 @@ describe('compiler: parse', () => {
type: NodeTypes.SIMPLE_EXPRESSION, type: NodeTypes.SIMPLE_EXPRESSION,
content: 'click', content: 'click',
isStatic: true, isStatic: true,
isConstant: true, constType: ConstantTypes.CAN_STRINGIFY,
loc: { loc: {
source: 'click', source: 'click',
@ -1201,7 +1202,7 @@ describe('compiler: parse', () => {
type: NodeTypes.SIMPLE_EXPRESSION, type: NodeTypes.SIMPLE_EXPRESSION,
content: 'a.b', content: 'a.b',
isStatic: false, isStatic: false,
isConstant: false, constType: ConstantTypes.NOT_CONSTANT,
loc: { loc: {
source: '[a.b]', source: '[a.b]',
@ -1238,7 +1239,7 @@ describe('compiler: parse', () => {
type: NodeTypes.SIMPLE_EXPRESSION, type: NodeTypes.SIMPLE_EXPRESSION,
content: 'a', content: 'a',
isStatic: true, isStatic: true,
isConstant: true, constType: ConstantTypes.CAN_STRINGIFY,
loc: { loc: {
source: 'a', source: 'a',
@ -1259,7 +1260,7 @@ describe('compiler: parse', () => {
type: NodeTypes.SIMPLE_EXPRESSION, type: NodeTypes.SIMPLE_EXPRESSION,
content: 'b', content: 'b',
isStatic: false, isStatic: false,
isConstant: false, constType: ConstantTypes.NOT_CONSTANT,
loc: { loc: {
start: { offset: 8, line: 1, column: 9 }, start: { offset: 8, line: 1, column: 9 },
@ -1286,7 +1287,7 @@ describe('compiler: parse', () => {
type: NodeTypes.SIMPLE_EXPRESSION, type: NodeTypes.SIMPLE_EXPRESSION,
content: 'a', content: 'a',
isStatic: true, isStatic: true,
isConstant: true, constType: ConstantTypes.CAN_STRINGIFY,
loc: { loc: {
source: 'a', source: 'a',
@ -1307,7 +1308,7 @@ describe('compiler: parse', () => {
type: NodeTypes.SIMPLE_EXPRESSION, type: NodeTypes.SIMPLE_EXPRESSION,
content: 'b', content: 'b',
isStatic: false, isStatic: false,
isConstant: false, constType: ConstantTypes.NOT_CONSTANT,
loc: { loc: {
start: { offset: 13, line: 1, column: 14 }, start: { offset: 13, line: 1, column: 14 },
@ -1334,7 +1335,7 @@ describe('compiler: parse', () => {
type: NodeTypes.SIMPLE_EXPRESSION, type: NodeTypes.SIMPLE_EXPRESSION,
content: 'a', content: 'a',
isStatic: true, isStatic: true,
isConstant: true, constType: ConstantTypes.CAN_STRINGIFY,
loc: { loc: {
source: 'a', source: 'a',
@ -1355,7 +1356,7 @@ describe('compiler: parse', () => {
type: NodeTypes.SIMPLE_EXPRESSION, type: NodeTypes.SIMPLE_EXPRESSION,
content: 'b', content: 'b',
isStatic: false, isStatic: false,
isConstant: false, constType: ConstantTypes.NOT_CONSTANT,
loc: { loc: {
start: { offset: 8, line: 1, column: 9 }, start: { offset: 8, line: 1, column: 9 },
@ -1382,7 +1383,7 @@ describe('compiler: parse', () => {
type: NodeTypes.SIMPLE_EXPRESSION, type: NodeTypes.SIMPLE_EXPRESSION,
content: 'a', content: 'a',
isStatic: true, isStatic: true,
isConstant: true, constType: ConstantTypes.CAN_STRINGIFY,
loc: { loc: {
source: 'a', source: 'a',
@ -1403,7 +1404,7 @@ describe('compiler: parse', () => {
type: NodeTypes.SIMPLE_EXPRESSION, type: NodeTypes.SIMPLE_EXPRESSION,
content: 'b', content: 'b',
isStatic: false, isStatic: false,
isConstant: false, constType: ConstantTypes.NOT_CONSTANT,
loc: { loc: {
start: { offset: 14, line: 1, column: 15 }, start: { offset: 14, line: 1, column: 15 },
@ -1430,7 +1431,7 @@ describe('compiler: parse', () => {
type: NodeTypes.SIMPLE_EXPRESSION, type: NodeTypes.SIMPLE_EXPRESSION,
content: 'a', content: 'a',
isStatic: true, isStatic: true,
isConstant: true, constType: ConstantTypes.CAN_STRINGIFY,
loc: { loc: {
source: 'a', source: 'a',
start: { start: {
@ -1450,8 +1451,8 @@ describe('compiler: parse', () => {
type: NodeTypes.SIMPLE_EXPRESSION, type: NodeTypes.SIMPLE_EXPRESSION,
content: '{ b }', content: '{ b }',
isStatic: false, isStatic: false,
// The `isConstant` is the default value and will be determined in transformExpression // The `constType` is the default value and will be determined in transformExpression
isConstant: false, constType: ConstantTypes.NOT_CONSTANT,
loc: { loc: {
start: { offset: 10, line: 1, column: 11 }, start: { offset: 10, line: 1, column: 11 },
end: { offset: 15, line: 1, column: 16 }, end: { offset: 15, line: 1, column: 16 },
@ -1478,7 +1479,7 @@ describe('compiler: parse', () => {
type: NodeTypes.SIMPLE_EXPRESSION, type: NodeTypes.SIMPLE_EXPRESSION,
content: 'foo.bar', content: 'foo.bar',
isStatic: true, isStatic: true,
isConstant: true, constType: ConstantTypes.CAN_STRINGIFY,
loc: { loc: {
source: 'foo.bar', source: 'foo.bar',
start: { start: {

View File

@ -7,7 +7,8 @@ import {
VNodeCall, VNodeCall,
IfNode, IfNode,
ElementNode, ElementNode,
ForNode ForNode,
ConstantTypes
} from '../../src' } from '../../src'
import { FRAGMENT, RENDER_LIST, CREATE_TEXT } from '../../src/runtimeHelpers' import { FRAGMENT, RENDER_LIST, CREATE_TEXT } from '../../src/runtimeHelpers'
import { transformElement } from '../../src/transforms/transformElement' import { transformElement } from '../../src/transforms/transformElement'
@ -469,7 +470,7 @@ describe('compiler: hoistStatic transform', () => {
content: { content: {
content: `1`, content: `1`,
isStatic: false, isStatic: false,
isConstant: true constType: ConstantTypes.CAN_STRINGIFY
} }
} }
} }
@ -505,13 +506,13 @@ describe('compiler: hoistStatic transform', () => {
{ {
key: { key: {
content: `class`, content: `class`,
isConstant: true, isStatic: true,
isStatic: true constType: ConstantTypes.CAN_STRINGIFY
}, },
value: { value: {
content: `{ foo: true }`, content: `{ foo: true }`,
isConstant: true, isStatic: false,
isStatic: false constType: ConstantTypes.CAN_STRINGIFY
} }
} }
] ]
@ -534,8 +535,8 @@ describe('compiler: hoistStatic transform', () => {
type: NodeTypes.INTERPOLATION, type: NodeTypes.INTERPOLATION,
content: { content: {
content: `_ctx.bar`, content: `_ctx.bar`,
isConstant: false, isStatic: false,
isStatic: false constType: ConstantTypes.NOT_CONSTANT
} }
}, },
patchFlag: `1 /* TEXT */` patchFlag: `1 /* TEXT */`

View File

@ -5,7 +5,8 @@ import {
DirectiveNode, DirectiveNode,
NodeTypes, NodeTypes,
CompilerOptions, CompilerOptions,
InterpolationNode InterpolationNode,
ConstantTypes
} from '../../src' } from '../../src'
import { transformIf } from '../../src/transforms/vIf' import { transformIf } from '../../src/transforms/vIf'
import { transformExpression } from '../../src/transforms/transformExpression' import { transformExpression } from '../../src/transforms/transformExpression'
@ -408,7 +409,7 @@ describe('compiler: expression transform', () => {
type: NodeTypes.SIMPLE_EXPRESSION, type: NodeTypes.SIMPLE_EXPRESSION,
content: `13000n`, content: `13000n`,
isStatic: false, isStatic: false,
isConstant: true constType: ConstantTypes.CAN_STRINGIFY
}) })
}) })

View File

@ -12,7 +12,8 @@ import {
SimpleExpressionNode, SimpleExpressionNode,
ElementNode, ElementNode,
InterpolationNode, InterpolationNode,
ForCodegenNode ForCodegenNode,
ConstantTypes
} from '../../src/ast' } from '../../src/ast'
import { ErrorCodes } from '../../src/errors' import { ErrorCodes } from '../../src/errors'
import { CompilerOptions, generate } from '../../src' import { CompilerOptions, generate } from '../../src'
@ -760,7 +761,7 @@ describe('compiler: v-for', () => {
false /* disableTracking */ false /* disableTracking */
) )
).toMatchObject({ ).toMatchObject({
source: { content: `10`, isConstant: true }, source: { content: `10`, constType: ConstantTypes.CAN_STRINGIFY },
params: [{ content: `item` }], params: [{ content: `item` }],
innerVNodeCall: { innerVNodeCall: {
tag: `"p"`, tag: `"p"`,
@ -772,7 +773,7 @@ describe('compiler: v-for', () => {
type: NodeTypes.SIMPLE_EXPRESSION, type: NodeTypes.SIMPLE_EXPRESSION,
content: 'item', content: 'item',
isStatic: false, isStatic: false,
isConstant: false constType: ConstantTypes.NOT_CONSTANT
} }
}, },
patchFlag: genFlagText(PatchFlags.TEXT) patchFlag: genFlagText(PatchFlags.TEXT)

View File

@ -189,11 +189,23 @@ export interface DirectiveNode extends Node {
parseResult?: ForParseResult 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 { export interface SimpleExpressionNode extends Node {
type: NodeTypes.SIMPLE_EXPRESSION type: NodeTypes.SIMPLE_EXPRESSION
content: string content: string
isStatic: boolean isStatic: boolean
isConstant: boolean constType: ConstantTypes
/** /**
* Indicates this is an identifier for a hoist vnode call and points to the * Indicates this is an identifier for a hoist vnode call and points to the
* hoisted node. * hoisted node.
@ -204,11 +216,6 @@ export interface SimpleExpressionNode extends Node {
* the identifiers declared inside the function body. * the identifiers declared inside the function body.
*/ */
identifiers?: string[] 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 { export interface InterpolationNode extends Node {
@ -611,14 +618,14 @@ export function createSimpleExpression(
content: SimpleExpressionNode['content'], content: SimpleExpressionNode['content'],
isStatic: SimpleExpressionNode['isStatic'], isStatic: SimpleExpressionNode['isStatic'],
loc: SourceLocation = locStub, loc: SourceLocation = locStub,
isConstant: boolean = false constType: ConstantTypes = ConstantTypes.NOT_CONSTANT
): SimpleExpressionNode { ): SimpleExpressionNode {
return { return {
type: NodeTypes.SIMPLE_EXPRESSION, type: NodeTypes.SIMPLE_EXPRESSION,
loc, loc,
isConstant,
content, content,
isStatic isStatic,
constType: isStatic ? ConstantTypes.CAN_STRINGIFY : constType
} }
} }

View File

@ -22,7 +22,8 @@ import {
TextNode, TextNode,
TemplateChildNode, TemplateChildNode,
InterpolationNode, InterpolationNode,
createRoot createRoot,
ConstantTypes
} from './ast' } from './ast'
type OptionalOptions = 'isNativeTag' | 'isBuiltInComponent' type OptionalOptions = 'isNativeTag' | 'isBuiltInComponent'
@ -656,7 +657,9 @@ function parseAttribute(
type: NodeTypes.SIMPLE_EXPRESSION, type: NodeTypes.SIMPLE_EXPRESSION,
content, content,
isStatic, isStatic,
isConstant: isStatic, constType: isStatic
? ConstantTypes.CAN_STRINGIFY
: ConstantTypes.NOT_CONSTANT,
loc loc
} }
} }
@ -677,8 +680,8 @@ function parseAttribute(
content: value.content, content: value.content,
isStatic: false, isStatic: false,
// Treat as non-constant by default. This can be potentially set to // Treat as non-constant by default. This can be potentially set to
// true by `transformExpression` to make it eligible for hoisting. // other values by `transformExpression` to make it eligible for hoisting.
isConstant: false, constType: ConstantTypes.NOT_CONSTANT,
loc: value.loc loc: value.loc
}, },
arg, arg,
@ -785,7 +788,7 @@ function parseInterpolation(
type: NodeTypes.SIMPLE_EXPRESSION, type: NodeTypes.SIMPLE_EXPRESSION,
isStatic: false, isStatic: false,
// Set `isConstant` to false by default and will decide in transformExpression // Set `isConstant` to false by default and will decide in transformExpression
isConstant: false, constType: ConstantTypes.NOT_CONSTANT,
content, content,
loc: getSelection(context, innerStart, innerEnd) loc: getSelection(context, innerStart, innerEnd)
}, },

View File

@ -15,7 +15,8 @@ import {
CacheExpression, CacheExpression,
createCacheExpression, createCacheExpression,
TemplateLiteral, TemplateLiteral,
createVNodeCall createVNodeCall,
ConstantTypes
} from './ast' } from './ast'
import { import {
isString, isString,
@ -245,7 +246,7 @@ export function createTransformContext(
`_hoisted_${context.hoists.length}`, `_hoisted_${context.hoists.length}`,
false, false,
exp.loc, exp.loc,
true ConstantTypes.CAN_HOIST
) )
identifier.hoisted = exp identifier.hoisted = exp
return identifier return identifier

View File

@ -1,4 +1,5 @@
import { import {
ConstantTypes,
RootNode, RootNode,
NodeTypes, NodeTypes,
TemplateChildNode, TemplateChildNode,
@ -37,16 +38,10 @@ export function isSingleElementRoot(
) )
} }
const enum StaticType {
NOT_STATIC = 0,
FULL_STATIC,
HAS_RUNTIME_CONSTANT
}
function walk( function walk(
node: ParentNode, node: ParentNode,
context: TransformContext, context: TransformContext,
resultCache: Map<TemplateChildNode, StaticType>, resultCache: Map<TemplateChildNode, ConstantTypes>,
doNotHoistNode: boolean = false doNotHoistNode: boolean = false
) { ) {
let hasHoistedNode = false let hasHoistedNode = false
@ -58,7 +53,7 @@ function walk(
// @vue/compiler-dom), but doing it here allows us to perform only one full // @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 // walk of the AST and allow `stringifyStatic` to stop walking as soon as its
// stringficiation threshold is met. // stringficiation threshold is met.
let hasRuntimeConstant = false let canStringify = true
const { children } = node const { children } = node
for (let i = 0; i < children.length; i++) { for (let i = 0; i < children.length; i++) {
@ -68,20 +63,20 @@ function walk(
child.type === NodeTypes.ELEMENT && child.type === NodeTypes.ELEMENT &&
child.tagType === ElementTypes.ELEMENT child.tagType === ElementTypes.ELEMENT
) { ) {
let staticType const constantType = doNotHoistNode
if ( ? ConstantTypes.NOT_CONSTANT
!doNotHoistNode && : getConstantType(child, resultCache)
(staticType = getStaticType(child, resultCache)) > 0 if (constantType > ConstantTypes.NOT_CONSTANT) {
) { if (constantType < ConstantTypes.CAN_STRINGIFY) {
if (staticType === StaticType.HAS_RUNTIME_CONSTANT) { canStringify = false
hasRuntimeConstant = true }
if (constantType >= ConstantTypes.CAN_HOIST) {
;(child.codegenNode as VNodeCall).patchFlag =
PatchFlags.HOISTED + (__DEV__ ? ` /* HOISTED */` : ``)
child.codegenNode = context.hoist(child.codegenNode!)
hasHoistedNode = true
continue
} }
// whole tree is static
;(child.codegenNode as VNodeCall).patchFlag =
PatchFlags.HOISTED + (__DEV__ ? ` /* HOISTED */` : ``)
child.codegenNode = context.hoist(child.codegenNode!)
hasHoistedNode = true
continue
} else { } else {
// node may contain dynamic children, but its props may be eligible for // node may contain dynamic children, but its props may be eligible for
// hoisting. // hoisting.
@ -92,7 +87,8 @@ function walk(
(!flag || (!flag ||
flag === PatchFlags.NEED_PATCH || flag === PatchFlags.NEED_PATCH ||
flag === PatchFlags.TEXT) && flag === PatchFlags.TEXT) &&
!hasNonHoistableProps(child) getGeneratedPropsConstantType(child, resultCache) >=
ConstantTypes.CAN_HOIST
) { ) {
const props = getNodeProps(child) const props = getNodeProps(child)
if (props) { if (props) {
@ -102,13 +98,15 @@ function walk(
} }
} }
} else if (child.type === NodeTypes.TEXT_CALL) { } else if (child.type === NodeTypes.TEXT_CALL) {
const staticType = getStaticType(child.content, resultCache) const contentType = getConstantType(child.content, resultCache)
if (staticType > 0) { if (contentType > 0) {
if (staticType === StaticType.HAS_RUNTIME_CONSTANT) { if (contentType < ConstantTypes.CAN_STRINGIFY) {
hasRuntimeConstant = true canStringify = false
}
if (contentType >= ConstantTypes.CAN_HOIST) {
child.codegenNode = context.hoist(child.codegenNode)
hasHoistedNode = true
} }
child.codegenNode = context.hoist(child.codegenNode)
hasHoistedNode = true
} }
} }
@ -131,19 +129,19 @@ function walk(
} }
} }
if (!hasRuntimeConstant && hasHoistedNode && context.transformHoist) { if (canStringify && hasHoistedNode && context.transformHoist) {
context.transformHoist(children, context, node) context.transformHoist(children, context, node)
} }
} }
export function getStaticType( export function getConstantType(
node: TemplateChildNode | SimpleExpressionNode, node: TemplateChildNode | SimpleExpressionNode,
resultCache: Map<TemplateChildNode, StaticType> = new Map() resultCache: Map<TemplateChildNode, ConstantTypes> = new Map()
): StaticType { ): ConstantTypes {
switch (node.type) { switch (node.type) {
case NodeTypes.ELEMENT: case NodeTypes.ELEMENT:
if (node.tagType !== ElementTypes.ELEMENT) { if (node.tagType !== ElementTypes.ELEMENT) {
return StaticType.NOT_STATIC return ConstantTypes.NOT_CONSTANT
} }
const cached = resultCache.get(node) const cached = resultCache.get(node)
if (cached !== undefined) { if (cached !== undefined) {
@ -151,40 +149,64 @@ export function getStaticType(
} }
const codegenNode = node.codegenNode! const codegenNode = node.codegenNode!
if (codegenNode.type !== NodeTypes.VNODE_CALL) { if (codegenNode.type !== NodeTypes.VNODE_CALL) {
return StaticType.NOT_STATIC return ConstantTypes.NOT_CONSTANT
} }
const flag = getPatchFlag(codegenNode) const flag = getPatchFlag(codegenNode)
if (!flag && !hasNonHoistableProps(node)) { if (!flag) {
// element self is static. check its children. let returnType = ConstantTypes.CAN_STRINGIFY
let returnType = StaticType.FULL_STATIC
// 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++) { for (let i = 0; i < node.children.length; i++) {
const childType = getStaticType(node.children[i], resultCache) const childType = getConstantType(node.children[i], resultCache)
if (childType === StaticType.NOT_STATIC) { if (childType === ConstantTypes.NOT_CONSTANT) {
resultCache.set(node, StaticType.NOT_STATIC) resultCache.set(node, ConstantTypes.NOT_CONSTANT)
return StaticType.NOT_STATIC return ConstantTypes.NOT_CONSTANT
} else if (childType === StaticType.HAS_RUNTIME_CONSTANT) { }
returnType = StaticType.HAS_RUNTIME_CONSTANT if (childType < returnType) {
returnType = childType
} }
} }
// check if any of the props contain runtime constants // 3. if the type is not already CAN_SKIP_PATCH which is the lowest non-0
if (returnType !== StaticType.HAS_RUNTIME_CONSTANT) { // 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++) { for (let i = 0; i < node.props.length; i++) {
const p = node.props[i] const p = node.props[i]
if ( if (p.type === NodeTypes.DIRECTIVE && p.name === 'bind' && p.exp) {
p.type === NodeTypes.DIRECTIVE && const expType = getConstantType(p.exp, resultCache)
p.name === 'bind' && if (expType === ConstantTypes.NOT_CONSTANT) {
p.exp && resultCache.set(node, ConstantTypes.NOT_CONSTANT)
(p.exp.type === NodeTypes.COMPOUND_EXPRESSION || return ConstantTypes.NOT_CONSTANT
p.exp.isRuntimeConstant) }
) { if (expType < returnType) {
returnType = StaticType.HAS_RUNTIME_CONSTANT returnType = expType
}
} }
} }
} }
// only svg/foreignObject could be block here, however if they are // 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. // nested updates.
if (codegenNode.isBlock) { if (codegenNode.isBlock) {
codegenNode.isBlock = false codegenNode.isBlock = false
@ -193,37 +215,33 @@ export function getStaticType(
resultCache.set(node, returnType) resultCache.set(node, returnType)
return returnType return returnType
} else { } else {
resultCache.set(node, StaticType.NOT_STATIC) resultCache.set(node, ConstantTypes.NOT_CONSTANT)
return StaticType.NOT_STATIC return ConstantTypes.NOT_CONSTANT
} }
case NodeTypes.TEXT: case NodeTypes.TEXT:
case NodeTypes.COMMENT: case NodeTypes.COMMENT:
return StaticType.FULL_STATIC return ConstantTypes.CAN_STRINGIFY
case NodeTypes.IF: case NodeTypes.IF:
case NodeTypes.FOR: case NodeTypes.FOR:
case NodeTypes.IF_BRANCH: case NodeTypes.IF_BRANCH:
return StaticType.NOT_STATIC return ConstantTypes.NOT_CONSTANT
case NodeTypes.INTERPOLATION: case NodeTypes.INTERPOLATION:
case NodeTypes.TEXT_CALL: case NodeTypes.TEXT_CALL:
return getStaticType(node.content, resultCache) return getConstantType(node.content, resultCache)
case NodeTypes.SIMPLE_EXPRESSION: case NodeTypes.SIMPLE_EXPRESSION:
return node.isRuntimeConstant return node.constType
? StaticType.HAS_RUNTIME_CONSTANT
: node.isConstant
? StaticType.FULL_STATIC
: StaticType.NOT_STATIC
case NodeTypes.COMPOUND_EXPRESSION: case NodeTypes.COMPOUND_EXPRESSION:
let returnType = StaticType.FULL_STATIC let returnType = ConstantTypes.CAN_STRINGIFY
for (let i = 0; i < node.children.length; i++) { for (let i = 0; i < node.children.length; i++) {
const child = node.children[i] const child = node.children[i]
if (isString(child) || isSymbol(child)) { if (isString(child) || isSymbol(child)) {
continue continue
} }
const childType = getStaticType(child, resultCache) const childType = getConstantType(child, resultCache)
if (childType === StaticType.NOT_STATIC) { if (childType === ConstantTypes.NOT_CONSTANT) {
return StaticType.NOT_STATIC return ConstantTypes.NOT_CONSTANT
} else if (childType === StaticType.HAS_RUNTIME_CONSTANT) { } else if (childType < returnType) {
returnType = StaticType.HAS_RUNTIME_CONSTANT returnType = childType
} }
} }
return returnType return returnType
@ -232,33 +250,40 @@ export function getStaticType(
const exhaustiveCheck: never = node const exhaustiveCheck: never = node
exhaustiveCheck exhaustiveCheck
} }
return StaticType.NOT_STATIC return ConstantTypes.NOT_CONSTANT
} }
} }
/** function getGeneratedPropsConstantType(
* Even for a node with no patch flag, it is possible for it to contain node: PlainElementNode,
* non-hoistable expressions that refers to scope variables, e.g. compiler resultCache: Map<TemplateChildNode, ConstantTypes>
* injected keys or cached event handlers. Therefore we need to always check the ): ConstantTypes {
* codegenNode's props to be sure. let returnType = ConstantTypes.CAN_STRINGIFY
*/
function hasNonHoistableProps(node: PlainElementNode): boolean {
const props = getNodeProps(node) const props = getNodeProps(node)
if (props && props.type === NodeTypes.JS_OBJECT_EXPRESSION) { if (props && props.type === NodeTypes.JS_OBJECT_EXPRESSION) {
const { properties } = props const { properties } = props
for (let i = 0; i < properties.length; i++) { for (let i = 0; i < properties.length; i++) {
const { key, value } = properties[i] const { key, value } = properties[i]
if ( const keyType = getConstantType(key, resultCache)
key.type !== NodeTypes.SIMPLE_EXPRESSION || if (keyType === ConstantTypes.NOT_CONSTANT) {
!key.isStatic || return keyType
(value.type !== NodeTypes.SIMPLE_EXPRESSION || }
(!value.isStatic && !value.isConstant)) if (keyType < returnType) {
) { returnType = keyType
return true }
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) { function getNodeProps(node: PlainElementNode) {

View File

@ -18,7 +18,8 @@ import {
VNodeCall, VNodeCall,
TemplateTextChildNode, TemplateTextChildNode,
DirectiveArguments, DirectiveArguments,
createVNodeCall createVNodeCall,
ConstantTypes
} from '../ast' } from '../ast'
import { import {
PatchFlags, PatchFlags,
@ -53,7 +54,7 @@ import {
isStaticExp isStaticExp
} from '../utils' } from '../utils'
import { buildSlots } from './vSlot' import { buildSlots } from './vSlot'
import { getStaticType } from './hoistStatic' import { getConstantType } from './hoistStatic'
import { BindingTypes } from '../options' import { BindingTypes } from '../options'
// some directive transforms (e.g. v-model) may return a symbol for runtime // 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 = const hasDynamicTextChild =
type === NodeTypes.INTERPOLATION || type === NodeTypes.INTERPOLATION ||
type === NodeTypes.COMPOUND_EXPRESSION type === NodeTypes.COMPOUND_EXPRESSION
if (hasDynamicTextChild && !getStaticType(child)) { if (
hasDynamicTextChild &&
getConstantType(child) === ConstantTypes.NOT_CONSTANT
) {
patchFlag |= PatchFlags.TEXT patchFlag |= PatchFlags.TEXT
} }
// pass directly if the only child is a text node // 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.JS_CACHE_EXPRESSION ||
((value.type === NodeTypes.SIMPLE_EXPRESSION || ((value.type === NodeTypes.SIMPLE_EXPRESSION ||
value.type === NodeTypes.COMPOUND_EXPRESSION) && value.type === NodeTypes.COMPOUND_EXPRESSION) &&
getStaticType(value) > 0) getConstantType(value) > 0)
) { ) {
// skip if the prop is a cached handler or has constant value // skip if the prop is a cached handler or has constant value
return return

View File

@ -14,7 +14,8 @@ import {
ExpressionNode, ExpressionNode,
SimpleExpressionNode, SimpleExpressionNode,
CompoundExpressionNode, CompoundExpressionNode,
createCompoundExpression createCompoundExpression,
ConstantTypes
} from '../ast' } from '../ast'
import { advancePositionWithClone, isSimpleIdentifier } from '../utils' import { advancePositionWithClone, isSimpleIdentifier } from '../utils'
import { import {
@ -190,25 +191,26 @@ export function processExpression(
// fast path if expression is a simple identifier. // fast path if expression is a simple identifier.
const rawExp = node.content const rawExp = node.content
// bail on parens to prevent any possible function invocations. // bail constant on parens (function invocation) and dot (member access)
const bailConstant = rawExp.indexOf(`(`) > -1 const bailConstant = rawExp.indexOf(`(`) > -1 || rawExp.indexOf('.') > 0
if (isSimpleIdentifier(rawExp)) { if (isSimpleIdentifier(rawExp)) {
// const bindings exposed from setup - we know they never change const isScopeVarReference = context.identifiers[rawExp]
// marking it as runtime constant will prevent it from being listed as const isAllowedGlobal = isGloballyWhitelisted(rawExp)
// a dynamic prop. const isLiteral = isLiteralWhitelisted(rawExp)
if (bindingMetadata[node.content] === BindingTypes.SETUP_CONST) { if (!asParams && !isScopeVarReference && !isAllowedGlobal && !isLiteral) {
node.isRuntimeConstant = true // const bindings exposed from setup can be skipped for patching but
} // cannot be hoisted to module scope
if ( if (bindingMetadata[node.content] === BindingTypes.SETUP_CONST) {
!asParams && node.constType = ConstantTypes.CAN_SKIP_PATCH
!context.identifiers[rawExp] && }
!isGloballyWhitelisted(rawExp) &&
!isLiteralWhitelisted(rawExp)
) {
node.content = rewriteIdentifier(rawExp) node.content = rewriteIdentifier(rawExp)
} else if (!context.identifiers[rawExp] && !bailConstant) { } else if (!isScopeVarReference) {
// mark node constant for hoisting unless it's referring a scope variable if (isLiteral) {
node.isConstant = true node.constType = ConstantTypes.CAN_STRINGIFY
} else {
node.constType = ConstantTypes.CAN_HOIST
}
} }
return node return node
} }
@ -342,7 +344,7 @@ export function processExpression(
start: advancePositionWithClone(node.loc.start, source, start), start: advancePositionWithClone(node.loc.start, source, start),
end: advancePositionWithClone(node.loc.start, source, end) 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) { if (i === ids.length - 1 && end < rawExp.length) {
@ -355,7 +357,9 @@ export function processExpression(
ret = createCompoundExpression(children, node.loc) ret = createCompoundExpression(children, node.loc)
} else { } else {
ret = node ret = node
ret.isConstant = !bailConstant ret.constType = bailConstant
? ConstantTypes.NOT_CONSTANT
: ConstantTypes.CAN_STRINGIFY
} }
ret.identifiers = Object.keys(knownIds) ret.identifiers = Object.keys(knownIds)
return ret return ret

View File

@ -4,11 +4,13 @@ import {
CompoundExpressionNode, CompoundExpressionNode,
createCallExpression, createCallExpression,
CallExpression, CallExpression,
ElementTypes ElementTypes,
ConstantTypes
} from '../ast' } from '../ast'
import { isText } from '../utils' import { isText } from '../utils'
import { CREATE_TEXT } from '../runtimeHelpers' import { CREATE_TEXT } from '../runtimeHelpers'
import { PatchFlags, PatchFlagNames } from '@vue/shared' import { PatchFlags, PatchFlagNames } from '@vue/shared'
import { getConstantType } from './hoistStatic'
// Merge adjacent text nodes and expressions into a single expression // 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. // 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) callArgs.push(child)
} }
// mark dynamic text with flag so it gets patched inside a block // 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( callArgs.push(
`${PatchFlags.TEXT} /* ${PatchFlagNames[PatchFlags.TEXT]} */` `${PatchFlags.TEXT} /* ${PatchFlagNames[PatchFlags.TEXT]} */`
) )

View File

@ -77,7 +77,7 @@ export const transformFor = createStructuralDirectiveTransform(
const isStableFragment = const isStableFragment =
forNode.source.type === NodeTypes.SIMPLE_EXPRESSION && forNode.source.type === NodeTypes.SIMPLE_EXPRESSION &&
forNode.source.isConstant forNode.source.constType > 0
const fragmentFlag = isStableFragment const fragmentFlag = isStableFragment
? PatchFlags.STABLE_FRAGMENT ? PatchFlags.STABLE_FRAGMENT
: keyProp : keyProp

View File

@ -21,7 +21,8 @@ import {
createVNodeCall, createVNodeCall,
AttributeNode, AttributeNode,
locStub, locStub,
CacheExpression CacheExpression,
ConstantTypes
} from '../ast' } from '../ast'
import { createCompilerError, ErrorCodes } from '../errors' import { createCompilerError, ErrorCodes } from '../errors'
import { processExpression } from './transformExpression' import { processExpression } from './transformExpression'
@ -227,7 +228,12 @@ function createChildrenCodegenNode(
const { helper } = context const { helper } = context
const keyProperty = createObjectProperty( const keyProperty = createObjectProperty(
`key`, `key`,
createSimpleExpression(`${keyIndex}`, false, locStub, true) createSimpleExpression(
`${keyIndex}`,
false,
locStub,
ConstantTypes.CAN_HOIST
)
) )
const { children } = branch const { children } = branch
const firstChild = children[0] const firstChild = children[0]

View File

@ -6,7 +6,8 @@ import {
NodeTypes, NodeTypes,
Property, Property,
ElementTypes, ElementTypes,
ExpressionNode ExpressionNode,
ConstantTypes
} from '../ast' } from '../ast'
import { createCompilerError, ErrorCodes } from '../errors' import { createCompilerError, ErrorCodes } from '../errors'
import { import {
@ -125,7 +126,12 @@ export const transformModel: DirectiveTransform = (dir, node, context) => {
props.push( props.push(
createObjectProperty( createObjectProperty(
modifiersKey, modifiersKey,
createSimpleExpression(`{ ${modifiers} }`, false, dir.loc, true) createSimpleExpression(
`{ ${modifiers} }`,
false,
dir.loc,
ConstantTypes.CAN_HOIST
)
) )
) )
} }

View File

@ -87,7 +87,7 @@ export const transformOn: DirectiveTransform = (
context.cacheHandlers && context.cacheHandlers &&
// runtime constants don't need to be cached // runtime constants don't need to be cached
// (this is analyzed by compileScript in SFC <script setup>) // (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 - // #1541 bail if this is a member exp handler passed to a component -
// we need to use the original function to preserve arity, // we need to use the original function to preserve arity,
// e.g. <transition> relies on checking cb.length to determine // e.g. <transition> relies on checking cb.length to determine

View File

@ -6,7 +6,8 @@ import {
ErrorCodes, ErrorCodes,
ElementTypes, ElementTypes,
InterpolationNode, InterpolationNode,
AttributeNode AttributeNode,
ConstantTypes
} from '@vue/compiler-core' } from '@vue/compiler-core'
import { parserOptions, DOMNamespaces } from '../src/parserOptions' import { parserOptions, DOMNamespaces } from '../src/parserOptions'
@ -253,7 +254,7 @@ describe('DOM parser', () => {
type: NodeTypes.SIMPLE_EXPRESSION, type: NodeTypes.SIMPLE_EXPRESSION,
content: `a < b`, content: `a < b`,
isStatic: false, isStatic: false,
isConstant: false, constType: ConstantTypes.NOT_CONSTANT,
loc: { loc: {
start: { offset: 8, line: 1, column: 9 }, start: { offset: 8, line: 1, column: 9 },
end: { offset: 16, line: 1, column: 17 }, end: { offset: 16, line: 1, column: 17 },

View File

@ -2,7 +2,8 @@ import {
compile, compile,
NodeTypes, NodeTypes,
CREATE_STATIC, CREATE_STATIC,
createSimpleExpression createSimpleExpression,
ConstantTypes
} from '../../src' } from '../../src'
import { import {
stringifyStatic, stringifyStatic,
@ -176,9 +177,8 @@ describe('stringify static html', () => {
'_imports_0_', '_imports_0_',
false, false,
node.loc, node.loc,
true ConstantTypes.CAN_HOIST
) )
exp.isRuntimeConstant = true
node.props[0] = { node.props[0] = {
type: NodeTypes.DIRECTIVE, type: NodeTypes.DIRECTIVE,
name: 'bind', name: 'bind',

View File

@ -3,7 +3,8 @@ import {
NodeTypes, NodeTypes,
createSimpleExpression, createSimpleExpression,
SimpleExpressionNode, SimpleExpressionNode,
SourceLocation SourceLocation,
ConstantTypes
} from '@vue/compiler-core' } from '@vue/compiler-core'
import { parseStringStyle } from '@vue/shared' import { parseStringStyle } from '@vue/shared'
@ -36,5 +37,10 @@ const parseInlineCSS = (
loc: SourceLocation loc: SourceLocation
): SimpleExpressionNode => { ): SimpleExpressionNode => {
const normalized = parseStringStyle(cssText) const normalized = parseStringStyle(cssText)
return createSimpleExpression(JSON.stringify(normalized), false, loc, true) return createSimpleExpression(
JSON.stringify(normalized),
false,
loc,
ConstantTypes.CAN_STRINGIFY
)
} }

View File

@ -1,5 +1,6 @@
import path from 'path' import path from 'path'
import { import {
ConstantTypes,
createSimpleExpression, createSimpleExpression,
ExpressionNode, ExpressionNode,
NodeTransform, NodeTransform,
@ -159,19 +160,26 @@ function getImportsExpressionExp(
return existing.exp as ExpressionNode return existing.exp as ExpressionNode
} }
const name = `_imports_${importsArray.length}` const name = `_imports_${importsArray.length}`
const exp = createSimpleExpression(name, false, loc, true) const exp = createSimpleExpression(
exp.isRuntimeConstant = true name,
false,
loc,
ConstantTypes.CAN_HOIST
)
context.imports.add({ exp, path }) context.imports.add({ exp, path })
if (hash && path) { if (hash && path) {
const ret = context.hoist( return context.hoist(
createSimpleExpression(`${name} + '${hash}'`, false, loc, true) createSimpleExpression(
`${name} + '${hash}'`,
false,
loc,
ConstantTypes.CAN_HOIST
)
) )
ret.isRuntimeConstant = true
return ret
} else { } else {
return exp return exp
} }
} else { } else {
return createSimpleExpression(`''`, false, loc, true) return createSimpleExpression(`''`, false, loc, ConstantTypes.CAN_HOIST)
} }
} }

View File

@ -1,5 +1,6 @@
import path from 'path' import path from 'path'
import { import {
ConstantTypes,
createCompoundExpression, createCompoundExpression,
createSimpleExpression, createSimpleExpression,
NodeTransform, NodeTransform,
@ -107,14 +108,14 @@ export const transformSrcset: NodeTransform = (
`_imports_${existingImportsIndex}`, `_imports_${existingImportsIndex}`,
false, false,
attr.loc, attr.loc,
true ConstantTypes.CAN_HOIST
) )
} else { } else {
exp = createSimpleExpression( exp = createSimpleExpression(
`_imports_${importsArray.length}`, `_imports_${importsArray.length}`,
false, false,
attr.loc, attr.loc,
true ConstantTypes.CAN_HOIST
) )
context.imports.add({ exp, path }) context.imports.add({ exp, path })
} }
@ -125,7 +126,7 @@ export const transformSrcset: NodeTransform = (
`"${url}"`, `"${url}"`,
false, false,
attr.loc, attr.loc,
true ConstantTypes.CAN_HOIST
) )
compoundExpression.children.push(exp) compoundExpression.children.push(exp)
} }
@ -140,7 +141,7 @@ export const transformSrcset: NodeTransform = (
}) })
const hoisted = context.hoist(compoundExpression) const hoisted = context.hoist(compoundExpression)
hoisted.isRuntimeConstant = true hoisted.constType = ConstantTypes.CAN_HOIST
node.props[index] = { node.props[index] = {
type: NodeTypes.DIRECTIVE, type: NodeTypes.DIRECTIVE,