refactor(compiler): use symbols for runtime helpers
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { isString } from '@vue/shared'
|
||||
import { ForParseResult } from './transforms/vFor'
|
||||
import { CREATE_VNODE, RuntimeHelper } from './runtimeHelpers'
|
||||
|
||||
// Vue template is a platform-agnostic superset of HTML (syntax only).
|
||||
// More namespaces like SVG and MathML are declared by platform specific
|
||||
@@ -76,8 +77,9 @@ export type TemplateChildNode =
|
||||
export interface RootNode extends Node {
|
||||
type: NodeTypes.ROOT
|
||||
children: TemplateChildNode[]
|
||||
imports: string[]
|
||||
statements: string[]
|
||||
helpers: RuntimeHelper[]
|
||||
components: string[]
|
||||
directives: string[]
|
||||
hoists: JSChildNode[]
|
||||
codegenNode: TemplateChildNode | JSChildNode | undefined
|
||||
}
|
||||
@@ -93,6 +95,29 @@ export interface ElementNode extends Node {
|
||||
codegenNode: CallExpression | SimpleExpressionNode | undefined
|
||||
}
|
||||
|
||||
export interface PlainElementNode extends ElementNode {
|
||||
tagType: ElementTypes.ELEMENT
|
||||
codegenNode: VNodeCodegenNode | VNodeWithDirectiveCodegenNode
|
||||
}
|
||||
|
||||
export interface ComponentNode extends ElementNode {
|
||||
tagType: ElementTypes.COMPONENT
|
||||
codegenNode: VNodeCodegenNode | VNodeWithDirectiveCodegenNode
|
||||
}
|
||||
|
||||
export interface SlotOutletNode extends ElementNode {
|
||||
tagType: ElementTypes.SLOT
|
||||
codegenNode: SlotOutletCodegenNode
|
||||
}
|
||||
|
||||
export interface VNodeCodegenNode extends CallExpression {
|
||||
callee: typeof CREATE_VNODE
|
||||
}
|
||||
|
||||
export interface VNodeWithDirectiveCodegenNode extends CallExpression {}
|
||||
|
||||
export interface SlotOutletCodegenNode extends CallExpression {}
|
||||
|
||||
export interface TextNode extends Node {
|
||||
type: NodeTypes.TEXT
|
||||
content: string
|
||||
@@ -137,7 +162,12 @@ export interface InterpolationNode extends Node {
|
||||
// always dynamic
|
||||
export interface CompoundExpressionNode extends Node {
|
||||
type: NodeTypes.COMPOUND_EXPRESSION
|
||||
children: (SimpleExpressionNode | InterpolationNode | TextNode | string)[]
|
||||
children: (
|
||||
| SimpleExpressionNode
|
||||
| InterpolationNode
|
||||
| TextNode
|
||||
| string
|
||||
| RuntimeHelper)[]
|
||||
// an expression parsed as the params of a function will track
|
||||
// the identifiers declared inside the function body.
|
||||
identifiers?: string[]
|
||||
@@ -146,9 +176,11 @@ export interface CompoundExpressionNode extends Node {
|
||||
export interface IfNode extends Node {
|
||||
type: NodeTypes.IF
|
||||
branches: IfBranchNode[]
|
||||
codegenNode: SequenceExpression
|
||||
codegenNode: IfCodegenNode
|
||||
}
|
||||
|
||||
export interface IfCodegenNode extends SequenceExpression {}
|
||||
|
||||
export interface IfBranchNode extends Node {
|
||||
type: NodeTypes.IF_BRANCH
|
||||
condition: ExpressionNode | undefined // else
|
||||
@@ -162,9 +194,11 @@ export interface ForNode extends Node {
|
||||
keyAlias: ExpressionNode | undefined
|
||||
objectIndexAlias: ExpressionNode | undefined
|
||||
children: TemplateChildNode[]
|
||||
codegenNode: SequenceExpression
|
||||
codegenNode: ForCodegenNode
|
||||
}
|
||||
|
||||
export interface ForCodegenNode extends SequenceExpression {}
|
||||
|
||||
// We also include a number of JavaScript AST nodes for code generation.
|
||||
// The AST is an intentionally minimal subset just to meet the exact needs of
|
||||
// Vue render function generation.
|
||||
@@ -179,8 +213,13 @@ export type JSChildNode =
|
||||
|
||||
export interface CallExpression extends Node {
|
||||
type: NodeTypes.JS_CALL_EXPRESSION
|
||||
callee: string
|
||||
arguments: (string | JSChildNode | TemplateChildNode | TemplateChildNode[])[]
|
||||
callee: string | RuntimeHelper
|
||||
arguments: (
|
||||
| string
|
||||
| RuntimeHelper
|
||||
| JSChildNode
|
||||
| TemplateChildNode
|
||||
| TemplateChildNode[])[]
|
||||
}
|
||||
|
||||
export interface ObjectExpression extends Node {
|
||||
|
||||
@@ -23,10 +23,19 @@ import {
|
||||
advancePositionWithMutation,
|
||||
assert,
|
||||
isSimpleIdentifier,
|
||||
loadDep
|
||||
loadDep,
|
||||
toValidAssetId
|
||||
} from './utils'
|
||||
import { isString, isArray } from '@vue/shared'
|
||||
import { TO_STRING, CREATE_VNODE, COMMENT } from './runtimeConstants'
|
||||
import { isString, isArray, isSymbol } from '@vue/shared'
|
||||
import {
|
||||
TO_STRING,
|
||||
CREATE_VNODE,
|
||||
COMMENT,
|
||||
helperNameMap,
|
||||
RESOLVE_COMPONENT,
|
||||
RESOLVE_DIRECTIVE,
|
||||
RuntimeHelper
|
||||
} from './runtimeHelpers'
|
||||
|
||||
type CodegenNode = TemplateChildNode | JSChildNode
|
||||
|
||||
@@ -65,7 +74,7 @@ export interface CodegenContext extends Required<CodegenOptions> {
|
||||
offset: number
|
||||
indentLevel: number
|
||||
map?: SourceMapGenerator
|
||||
helper(name: string): string
|
||||
helper(key: RuntimeHelper): string
|
||||
push(code: string, node?: CodegenNode, openOnly?: boolean): void
|
||||
resetMapping(loc: SourceLocation): void
|
||||
indent(): void
|
||||
@@ -77,7 +86,7 @@ function createCodegenContext(
|
||||
ast: RootNode,
|
||||
{
|
||||
mode = 'function',
|
||||
prefixIdentifiers = false,
|
||||
prefixIdentifiers = mode === 'module',
|
||||
sourceMap = false,
|
||||
filename = `template.vue.html`
|
||||
}: CodegenOptions
|
||||
@@ -100,7 +109,8 @@ function createCodegenContext(
|
||||
? undefined
|
||||
: new (loadDep('source-map')).SourceMapGenerator(),
|
||||
|
||||
helper(name) {
|
||||
helper(key) {
|
||||
const name = helperNameMap[key]
|
||||
return prefixIdentifiers ? name : `_${name}`
|
||||
},
|
||||
push(code, node, openOnly) {
|
||||
@@ -172,8 +182,16 @@ export function generate(
|
||||
options: CodegenOptions = {}
|
||||
): CodegenResult {
|
||||
const context = createCodegenContext(ast, options)
|
||||
const { mode, push, prefixIdentifiers, indent, deindent, newline } = context
|
||||
const hasImports = ast.imports.length
|
||||
const {
|
||||
mode,
|
||||
push,
|
||||
helper,
|
||||
prefixIdentifiers,
|
||||
indent,
|
||||
deindent,
|
||||
newline
|
||||
} = context
|
||||
const hasHelpers = ast.helpers.length > 0
|
||||
const useWithBlock = !prefixIdentifiers && mode !== 'module'
|
||||
|
||||
// preambles
|
||||
@@ -182,9 +200,9 @@ export function generate(
|
||||
// In prefix mode, we place the const declaration at top so it's done
|
||||
// only once; But if we not prefixing, we place the declaration inside the
|
||||
// with block so it doesn't incur the `in` check cost for every helper access.
|
||||
if (hasImports) {
|
||||
if (hasHelpers) {
|
||||
if (prefixIdentifiers) {
|
||||
push(`const { ${ast.imports.join(', ')} } = Vue\n`)
|
||||
push(`const { ${ast.helpers.map(helper).join(', ')} } = Vue\n`)
|
||||
} else {
|
||||
// "with" mode.
|
||||
// save Vue in a separate variable to avoid collision
|
||||
@@ -193,7 +211,7 @@ export function generate(
|
||||
// has check cost, but hoists are lifted out of the function - we need
|
||||
// to provide the helper here.
|
||||
if (ast.hoists.length) {
|
||||
push(`const _${CREATE_VNODE} = Vue.createVNode\n`)
|
||||
push(`const _${helperNameMap[CREATE_VNODE]} = Vue.createVNode\n`)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -202,8 +220,8 @@ export function generate(
|
||||
push(`return `)
|
||||
} else {
|
||||
// generate import statements for helpers
|
||||
if (hasImports) {
|
||||
push(`import { ${ast.imports.join(', ')} } from "vue"\n`)
|
||||
if (hasHelpers) {
|
||||
push(`import { ${ast.helpers.map(helper).join(', ')} } from "vue"\n`)
|
||||
}
|
||||
genHoists(ast.hoists, context)
|
||||
context.newline()
|
||||
@@ -219,8 +237,12 @@ export function generate(
|
||||
indent()
|
||||
// function mode const declarations should be inside with block
|
||||
// also they should be renamed to avoid collision with user properties
|
||||
if (hasImports) {
|
||||
push(`const { ${ast.imports.map(n => `${n}: _${n}`).join(', ')} } = _Vue`)
|
||||
if (hasHelpers) {
|
||||
push(
|
||||
`const { ${ast.helpers
|
||||
.map(s => `${helperNameMap[s]}: _${helperNameMap[s]}`)
|
||||
.join(', ')} } = _Vue`
|
||||
)
|
||||
newline()
|
||||
newline()
|
||||
}
|
||||
@@ -230,11 +252,13 @@ export function generate(
|
||||
}
|
||||
|
||||
// generate asset resolution statements
|
||||
if (ast.statements.length) {
|
||||
ast.statements.forEach(s => {
|
||||
push(s)
|
||||
newline()
|
||||
})
|
||||
if (ast.components.length) {
|
||||
genAssets(ast.components, 'component', context)
|
||||
}
|
||||
if (ast.directives.length) {
|
||||
genAssets(ast.directives, 'directive', context)
|
||||
}
|
||||
if (ast.components.length || ast.directives.length) {
|
||||
newline()
|
||||
}
|
||||
|
||||
@@ -260,6 +284,23 @@ export function generate(
|
||||
}
|
||||
}
|
||||
|
||||
function genAssets(
|
||||
assets: string[],
|
||||
type: 'component' | 'directive',
|
||||
context: CodegenContext
|
||||
) {
|
||||
const resolver = context.helper(
|
||||
type === 'component' ? RESOLVE_COMPONENT : RESOLVE_DIRECTIVE
|
||||
)
|
||||
for (let i = 0; i < assets.length; i++) {
|
||||
const id = assets[i]
|
||||
context.push(
|
||||
`const ${toValidAssetId(id, type)} = ${resolver}(${JSON.stringify(id)})`
|
||||
)
|
||||
context.newline()
|
||||
}
|
||||
}
|
||||
|
||||
function genHoists(hoists: JSChildNode[], context: CodegenContext) {
|
||||
if (!hoists.length) {
|
||||
return
|
||||
@@ -297,7 +338,7 @@ function genNodeListAsArray(
|
||||
}
|
||||
|
||||
function genNodeList(
|
||||
nodes: (string | CodegenNode | TemplateChildNode[])[],
|
||||
nodes: (string | RuntimeHelper | CodegenNode | TemplateChildNode[])[],
|
||||
context: CodegenContext,
|
||||
multilines: boolean = false
|
||||
) {
|
||||
@@ -322,11 +363,18 @@ function genNodeList(
|
||||
}
|
||||
}
|
||||
|
||||
function genNode(node: CodegenNode | string, context: CodegenContext) {
|
||||
function genNode(
|
||||
node: CodegenNode | RuntimeHelper | string,
|
||||
context: CodegenContext
|
||||
) {
|
||||
if (isString(node)) {
|
||||
context.push(node)
|
||||
return
|
||||
}
|
||||
if (isSymbol(node)) {
|
||||
context.push(context.helper(node))
|
||||
return
|
||||
}
|
||||
switch (node.type) {
|
||||
case NodeTypes.ELEMENT:
|
||||
case NodeTypes.IF:
|
||||
@@ -450,7 +498,10 @@ function genComment(node: CommentNode, context: CodegenContext) {
|
||||
|
||||
// JavaScript
|
||||
function genCallExpression(node: CallExpression, context: CodegenContext) {
|
||||
context.push(node.callee + `(`, node, true)
|
||||
const callee = isString(node.callee)
|
||||
? node.callee
|
||||
: context.helper(node.callee)
|
||||
context.push(callee + `(`, node, true)
|
||||
genNodeList(node.arguments, context)
|
||||
context.push(`)`)
|
||||
}
|
||||
|
||||
@@ -64,7 +64,10 @@ export function baseCompile(
|
||||
}
|
||||
})
|
||||
|
||||
return generate(ast, options)
|
||||
return generate(ast, {
|
||||
...options,
|
||||
prefixIdentifiers
|
||||
})
|
||||
}
|
||||
|
||||
// Also expose lower level APIs & types
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
// Name mapping constants for runtime helpers that need to be imported in
|
||||
// generated code. Make sure these are correctly exported in the runtime!
|
||||
export const FRAGMENT = `Fragment`
|
||||
export const PORTAL = `Portal`
|
||||
export const COMMENT = `Comment`
|
||||
export const TEXT = `Text`
|
||||
export const SUSPENSE = `Suspense`
|
||||
export const EMPTY = `Empty`
|
||||
export const OPEN_BLOCK = `openBlock`
|
||||
export const CREATE_BLOCK = `createBlock`
|
||||
export const CREATE_VNODE = `createVNode`
|
||||
export const RESOLVE_COMPONENT = `resolveComponent`
|
||||
export const RESOLVE_DIRECTIVE = `resolveDirective`
|
||||
export const APPLY_DIRECTIVES = `applyDirectives`
|
||||
export const RENDER_LIST = `renderList`
|
||||
export const RENDER_SLOT = `renderSlot`
|
||||
export const CREATE_SLOTS = `createSlots`
|
||||
export const TO_STRING = `toString`
|
||||
export const MERGE_PROPS = `mergeProps`
|
||||
export const TO_HANDLERS = `toHandlers`
|
||||
export const CAMELIZE = `camelize`
|
||||
64
packages/compiler-core/src/runtimeHelpers.ts
Normal file
64
packages/compiler-core/src/runtimeHelpers.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
export const FRAGMENT = Symbol()
|
||||
export const PORTAL = Symbol()
|
||||
export const COMMENT = Symbol()
|
||||
export const TEXT = Symbol()
|
||||
export const SUSPENSE = Symbol()
|
||||
export const EMPTY = Symbol()
|
||||
export const OPEN_BLOCK = Symbol()
|
||||
export const CREATE_BLOCK = Symbol()
|
||||
export const CREATE_VNODE = Symbol()
|
||||
export const RESOLVE_COMPONENT = Symbol()
|
||||
export const RESOLVE_DIRECTIVE = Symbol()
|
||||
export const APPLY_DIRECTIVES = Symbol()
|
||||
export const RENDER_LIST = Symbol()
|
||||
export const RENDER_SLOT = Symbol()
|
||||
export const CREATE_SLOTS = Symbol()
|
||||
export const TO_STRING = Symbol()
|
||||
export const MERGE_PROPS = Symbol()
|
||||
export const TO_HANDLERS = Symbol()
|
||||
export const CAMELIZE = Symbol()
|
||||
|
||||
export type RuntimeHelper =
|
||||
| typeof FRAGMENT
|
||||
| typeof PORTAL
|
||||
| typeof COMMENT
|
||||
| typeof TEXT
|
||||
| typeof SUSPENSE
|
||||
| typeof EMPTY
|
||||
| typeof OPEN_BLOCK
|
||||
| typeof CREATE_BLOCK
|
||||
| typeof CREATE_VNODE
|
||||
| typeof RESOLVE_COMPONENT
|
||||
| typeof RESOLVE_DIRECTIVE
|
||||
| typeof APPLY_DIRECTIVES
|
||||
| typeof RENDER_LIST
|
||||
| typeof RENDER_SLOT
|
||||
| typeof CREATE_SLOTS
|
||||
| typeof TO_STRING
|
||||
| typeof MERGE_PROPS
|
||||
| typeof TO_HANDLERS
|
||||
| typeof CAMELIZE
|
||||
|
||||
// Name mapping for runtime helpers that need to be imported from 'vue' in
|
||||
// generated code. Make sure these are correctly exported in the runtime!
|
||||
export const helperNameMap = {
|
||||
[FRAGMENT]: `Fragment`,
|
||||
[PORTAL]: `Portal`,
|
||||
[COMMENT]: `Comment`,
|
||||
[TEXT]: `Text`,
|
||||
[SUSPENSE]: `Suspense`,
|
||||
[EMPTY]: `Empty`,
|
||||
[OPEN_BLOCK]: `openBlock`,
|
||||
[CREATE_BLOCK]: `createBlock`,
|
||||
[CREATE_VNODE]: `createVNode`,
|
||||
[RESOLVE_COMPONENT]: `resolveComponent`,
|
||||
[RESOLVE_DIRECTIVE]: `resolveDirective`,
|
||||
[APPLY_DIRECTIVES]: `applyDirectives`,
|
||||
[RENDER_LIST]: `renderList`,
|
||||
[RENDER_SLOT]: `renderSlot`,
|
||||
[CREATE_SLOTS]: `createSlots`,
|
||||
[TO_STRING]: `toString`,
|
||||
[MERGE_PROPS]: `mergeProps`,
|
||||
[TO_HANDLERS]: `toHandlers`,
|
||||
[CAMELIZE]: `camelize`
|
||||
}
|
||||
@@ -14,7 +14,14 @@ import {
|
||||
} from './ast'
|
||||
import { isString, isArray } from '@vue/shared'
|
||||
import { CompilerError, defaultOnError } from './errors'
|
||||
import { TO_STRING, COMMENT, CREATE_VNODE, FRAGMENT } from './runtimeConstants'
|
||||
import {
|
||||
TO_STRING,
|
||||
COMMENT,
|
||||
CREATE_VNODE,
|
||||
FRAGMENT,
|
||||
RuntimeHelper,
|
||||
helperNameMap
|
||||
} from './runtimeHelpers'
|
||||
import { isVSlot, createBlockExpression, isSlotOutlet } from './utils'
|
||||
import { hoistStatic } from './transforms/hoistStatic'
|
||||
|
||||
@@ -57,8 +64,9 @@ export interface TransformOptions {
|
||||
|
||||
export interface TransformContext extends Required<TransformOptions> {
|
||||
root: RootNode
|
||||
imports: Set<string>
|
||||
statements: Set<string>
|
||||
helpers: Set<RuntimeHelper>
|
||||
components: Set<string>
|
||||
directives: Set<string>
|
||||
hoists: JSChildNode[]
|
||||
identifiers: { [name: string]: number | undefined }
|
||||
scopes: {
|
||||
@@ -70,7 +78,8 @@ export interface TransformContext extends Required<TransformOptions> {
|
||||
parent: ParentNode | null
|
||||
childIndex: number
|
||||
currentNode: RootNode | TemplateChildNode | null
|
||||
helper(name: string): string
|
||||
helper<T extends RuntimeHelper>(name: T): T
|
||||
helperString(name: RuntimeHelper): string
|
||||
replaceNode(node: TemplateChildNode): void
|
||||
removeNode(node?: TemplateChildNode): void
|
||||
onNodeRemoved: () => void
|
||||
@@ -91,8 +100,9 @@ function createTransformContext(
|
||||
): TransformContext {
|
||||
const context: TransformContext = {
|
||||
root,
|
||||
imports: new Set(),
|
||||
statements: new Set(),
|
||||
helpers: new Set(),
|
||||
components: new Set(),
|
||||
directives: new Set(),
|
||||
hoists: [],
|
||||
identifiers: {},
|
||||
scopes: {
|
||||
@@ -110,8 +120,14 @@ function createTransformContext(
|
||||
currentNode: root,
|
||||
childIndex: 0,
|
||||
helper(name) {
|
||||
context.imports.add(name)
|
||||
return prefixIdentifiers ? name : `_${name}`
|
||||
context.helpers.add(name)
|
||||
return name
|
||||
},
|
||||
helperString(name) {
|
||||
return (
|
||||
(context.prefixIdentifiers ? `` : `_`) +
|
||||
helperNameMap[context.helper(name)]
|
||||
)
|
||||
},
|
||||
replaceNode(node) {
|
||||
/* istanbul ignore if */
|
||||
@@ -242,8 +258,9 @@ function finalizeRoot(root: RootNode, context: TransformContext) {
|
||||
}
|
||||
|
||||
// finalize meta information
|
||||
root.imports = [...context.imports]
|
||||
root.statements = [...context.statements]
|
||||
root.helpers = [...context.helpers]
|
||||
root.components = [...context.components]
|
||||
root.directives = [...context.directives]
|
||||
root.hoists = context.hoists
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
ElementTypes
|
||||
} from '../ast'
|
||||
import { TransformContext } from '../transform'
|
||||
import { APPLY_DIRECTIVES } from '../runtimeConstants'
|
||||
import { APPLY_DIRECTIVES } from '../runtimeHelpers'
|
||||
import { PropsExpression } from './transformElement'
|
||||
import { PatchFlags } from '@vue/shared'
|
||||
|
||||
@@ -41,7 +41,7 @@ function walk(
|
||||
flag === PatchFlags.TEXT
|
||||
) {
|
||||
let codegenNode = child.codegenNode as CallExpression
|
||||
if (codegenNode.callee.includes(APPLY_DIRECTIVES)) {
|
||||
if (codegenNode.callee === APPLY_DIRECTIVES) {
|
||||
codegenNode = codegenNode.arguments[0] as CallExpression
|
||||
}
|
||||
const props = codegenNode.arguments[1] as
|
||||
@@ -68,7 +68,7 @@ function walk(
|
||||
|
||||
function getPatchFlag(node: ElementNode): number | undefined {
|
||||
let codegenNode = node.codegenNode as CallExpression
|
||||
if (codegenNode.callee.includes(APPLY_DIRECTIVES)) {
|
||||
if (codegenNode.callee === APPLY_DIRECTIVES) {
|
||||
codegenNode = codegenNode.arguments[0] as CallExpression
|
||||
}
|
||||
const flag = codegenNode.arguments[3]
|
||||
|
||||
@@ -25,12 +25,10 @@ import {
|
||||
RESOLVE_COMPONENT,
|
||||
MERGE_PROPS,
|
||||
TO_HANDLERS
|
||||
} from '../runtimeConstants'
|
||||
import { getInnerRange, isVSlot } from '../utils'
|
||||
} from '../runtimeHelpers'
|
||||
import { getInnerRange, isVSlot, toValidAssetId } from '../utils'
|
||||
import { buildSlots } from './vSlot'
|
||||
|
||||
const toValidId = (str: string): string => str.replace(/[^\w]/g, '')
|
||||
|
||||
// generate a JavaScript AST for this element's codegen
|
||||
export const transformElement: NodeTransform = (node, context) => {
|
||||
if (node.type === NodeTypes.ELEMENT) {
|
||||
@@ -50,19 +48,14 @@ export const transformElement: NodeTransform = (node, context) => {
|
||||
let patchFlag: number = 0
|
||||
let runtimeDirectives: DirectiveNode[] | undefined
|
||||
let dynamicPropNames: string[] | undefined
|
||||
let componentIdentifier: string | undefined
|
||||
|
||||
if (isComponent) {
|
||||
componentIdentifier = `_component_${toValidId(node.tag)}`
|
||||
context.statements.add(
|
||||
`const ${componentIdentifier} = ${context.helper(
|
||||
RESOLVE_COMPONENT
|
||||
)}(${JSON.stringify(node.tag)})`
|
||||
)
|
||||
context.helper(RESOLVE_COMPONENT)
|
||||
context.components.add(node.tag)
|
||||
}
|
||||
|
||||
const args: CallExpression['arguments'] = [
|
||||
isComponent ? componentIdentifier! : `"${node.tag}"`
|
||||
isComponent ? toValidAssetId(node.tag, `component`) : `"${node.tag}"`
|
||||
]
|
||||
// props
|
||||
if (hasProps) {
|
||||
@@ -402,13 +395,11 @@ function createDirectiveArgs(
|
||||
context: TransformContext
|
||||
): ArrayExpression {
|
||||
// inject statement for resolving directive
|
||||
const dirIdentifier = `_directive_${toValidId(dir.name)}`
|
||||
context.statements.add(
|
||||
`const ${dirIdentifier} = ${context.helper(
|
||||
RESOLVE_DIRECTIVE
|
||||
)}(${JSON.stringify(dir.name)})`
|
||||
)
|
||||
const dirArgs: ArrayExpression['elements'] = [dirIdentifier]
|
||||
context.helper(RESOLVE_DIRECTIVE)
|
||||
context.directives.add(dir.name)
|
||||
const dirArgs: ArrayExpression['elements'] = [
|
||||
toValidAssetId(dir.name, `directive`)
|
||||
]
|
||||
const { loc } = dir
|
||||
if (dir.exp) dirArgs.push(dir.exp)
|
||||
if (dir.arg) dirArgs.push(dir.arg)
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
import { isSlotOutlet } from '../utils'
|
||||
import { buildProps } from './transformElement'
|
||||
import { createCompilerError, ErrorCodes } from '../errors'
|
||||
import { RENDER_SLOT } from '../runtimeConstants'
|
||||
import { RENDER_SLOT } from '../runtimeHelpers'
|
||||
|
||||
export const transformSlotOutlet: NodeTransform = (node, context) => {
|
||||
if (isSlotOutlet(node)) {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { DirectiveTransform } from '../transform'
|
||||
import { createObjectProperty, createSimpleExpression, NodeTypes } from '../ast'
|
||||
import { createCompilerError, ErrorCodes } from '../errors'
|
||||
import { camelize } from '@vue/shared'
|
||||
import { CAMELIZE } from '../runtimeConstants'
|
||||
import { CAMELIZE } from '../runtimeHelpers'
|
||||
|
||||
// v-bind without arg is handled directly in ./element.ts due to it affecting
|
||||
// codegen for the entire props object. This transform here is only for v-bind
|
||||
@@ -20,10 +20,10 @@ export const transformBind: DirectiveTransform = (dir, context) => {
|
||||
if (arg.isStatic) {
|
||||
arg.content = camelize(arg.content)
|
||||
} else {
|
||||
arg.content = `${context.helper(CAMELIZE)}(${arg.content})`
|
||||
arg.content = `${context.helperString(CAMELIZE)}(${arg.content})`
|
||||
}
|
||||
} else {
|
||||
arg.children.unshift(`${context.helper(CAMELIZE)}(`)
|
||||
arg.children.unshift(`${context.helperString(CAMELIZE)}(`)
|
||||
arg.children.push(`)`)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ import {
|
||||
OPEN_BLOCK,
|
||||
CREATE_BLOCK,
|
||||
FRAGMENT
|
||||
} from '../runtimeConstants'
|
||||
} from '../runtimeHelpers'
|
||||
import { processExpression } from './transformExpression'
|
||||
import { PatchFlags, PatchFlagNames } from '@vue/shared'
|
||||
import { PropsExpression } from './transformElement'
|
||||
|
||||
@@ -29,7 +29,7 @@ import {
|
||||
APPLY_DIRECTIVES,
|
||||
CREATE_VNODE,
|
||||
RENDER_SLOT
|
||||
} from '../runtimeConstants'
|
||||
} from '../runtimeHelpers'
|
||||
import { injectProp } from '../utils'
|
||||
import { PropsExpression } from './transformElement'
|
||||
|
||||
@@ -184,18 +184,18 @@ function createChildrenCodegenNode(
|
||||
const childCodegen = (child as ElementNode).codegenNode as CallExpression
|
||||
let vnodeCall = childCodegen
|
||||
// Element with custom directives. Locate the actual createVNode() call.
|
||||
if (vnodeCall.callee.includes(APPLY_DIRECTIVES)) {
|
||||
if (vnodeCall.callee === APPLY_DIRECTIVES) {
|
||||
vnodeCall = vnodeCall.arguments[0] as CallExpression
|
||||
}
|
||||
// Change createVNode to createBlock.
|
||||
if (vnodeCall.callee.includes(CREATE_VNODE)) {
|
||||
if (vnodeCall.callee === CREATE_VNODE) {
|
||||
vnodeCall.callee = helper(CREATE_BLOCK)
|
||||
}
|
||||
// It's possible to have renderSlot() here as well - which already produces
|
||||
// a block, so no need to change the callee. However it accepts props at
|
||||
// a different arg index so make sure to check for so that the key injection
|
||||
// logic below works for it too.
|
||||
const propsIndex = vnodeCall.callee.includes(RENDER_SLOT) ? 2 : 1
|
||||
const propsIndex = vnodeCall.callee === RENDER_SLOT ? 2 : 1
|
||||
// inject branch key
|
||||
const existingProps = vnodeCall.arguments[propsIndex] as
|
||||
| PropsExpression
|
||||
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
import { TransformContext, NodeTransform } from '../transform'
|
||||
import { createCompilerError, ErrorCodes } from '../errors'
|
||||
import { findDir, isTemplateNode, assert, isVSlot } from '../utils'
|
||||
import { CREATE_SLOTS, RENDER_LIST } from '../runtimeConstants'
|
||||
import { CREATE_SLOTS, RENDER_LIST } from '../runtimeHelpers'
|
||||
import { parseForExpression, createForLoopParams } from './vFor'
|
||||
|
||||
const isStaticExp = (p: JSChildNode): p is SimpleExpressionNode =>
|
||||
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
import { parse } from 'acorn'
|
||||
import { walk } from 'estree-walker'
|
||||
import { TransformContext } from './transform'
|
||||
import { OPEN_BLOCK, CREATE_BLOCK, MERGE_PROPS } from './runtimeConstants'
|
||||
import { OPEN_BLOCK, CREATE_BLOCK, MERGE_PROPS } from './runtimeHelpers'
|
||||
import { isString, isFunction } from '@vue/shared'
|
||||
import { PropsExpression } from './transforms/transformElement'
|
||||
|
||||
@@ -217,3 +217,10 @@ export function injectProp(
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
export function toValidAssetId(
|
||||
name: string,
|
||||
type: 'component' | 'directive'
|
||||
): string {
|
||||
return `_${type}_${name.replace(/[^\w]/g, '')}`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user