refactor: remove circular dependencies in compiler

This commit is contained in:
Evan You 2023-03-23 16:30:42 +08:00
parent 650f5c26f4
commit aac163110f
14 changed files with 240 additions and 239 deletions

View File

@ -7,11 +7,14 @@ import {
OPEN_BLOCK,
FRAGMENT,
WITH_DIRECTIVES,
WITH_MEMO
WITH_MEMO,
CREATE_VNODE,
CREATE_ELEMENT_VNODE,
CREATE_BLOCK,
CREATE_ELEMENT_BLOCK
} from './runtimeHelpers'
import { PropsExpression } from './transforms/transformElement'
import { ImportItem, TransformContext } from './transform'
import { getVNodeBlockHelper, getVNodeHelper } from './utils'
// Vue template is a platform-agnostic superset of HTML (syntax only).
// More namespaces like SVG and MathML are declared by platform specific
@ -810,3 +813,23 @@ export function createReturnStatement(
loc: locStub
}
}
export function getVNodeHelper(ssr: boolean, isComponent: boolean) {
return ssr || isComponent ? CREATE_VNODE : CREATE_ELEMENT_VNODE
}
export function getVNodeBlockHelper(ssr: boolean, isComponent: boolean) {
return ssr || isComponent ? CREATE_BLOCK : CREATE_ELEMENT_BLOCK
}
export function convertToBlock(
node: VNodeCall,
{ helper, removeHelper, inSSR }: TransformContext
) {
if (!node.isBlock) {
node.isBlock = true
removeHelper(getVNodeHelper(inSSR, node.isComponent))
helper(OPEN_BLOCK)
helper(getVNodeBlockHelper(inSSR, node.isComponent))
}
}

View File

@ -24,14 +24,14 @@ import {
AssignmentExpression,
ReturnStatement,
VNodeCall,
SequenceExpression
SequenceExpression,
getVNodeBlockHelper,
getVNodeHelper
} from './ast'
import { SourceMapGenerator, RawSourceMap } from 'source-map'
import {
advancePositionWithMutation,
assert,
getVNodeBlockHelper,
getVNodeHelper,
isSimpleIdentifier,
toValidAssetId
} from './utils'

View File

@ -1,19 +1,18 @@
import { RESOLVE_FILTER } from '../runtimeHelpers'
import {
ExpressionNode,
AttributeNode,
DirectiveNode,
NodeTransform,
NodeTypes,
SimpleExpressionNode,
toValidAssetId,
TransformContext
} from '@vue/compiler-core'
SimpleExpressionNode
} from '../ast'
import {
CompilerDeprecationTypes,
isCompatEnabled,
warnDeprecation
} from './compatConfig'
import { ExpressionNode } from '../ast'
import { NodeTransform, TransformContext } from '../transform'
import { toValidAssetId } from '../utils'
const validDivisionCharRE = /[\w).+\-_$\]]/

View File

@ -17,7 +17,8 @@ import {
TemplateLiteral,
createVNodeCall,
ConstantTypes,
ArrayExpression
ArrayExpression,
convertToBlock
} from './ast'
import {
isString,
@ -36,7 +37,7 @@ import {
helperNameMap,
CREATE_COMMENT
} from './runtimeHelpers'
import { isVSlot, makeBlock } from './utils'
import { isVSlot } from './utils'
import { hoistStatic, isSingleElementRoot } from './transforms/hoistStatic'
import { CompilerCompatOptions } from './compat/compatConfig'
@ -348,7 +349,7 @@ function createRootCodegen(root: RootNode, context: TransformContext) {
// SimpleExpressionNode
const codegenNode = child.codegenNode
if (codegenNode.type === NodeTypes.VNODE_CALL) {
makeBlock(codegenNode, context)
convertToBlock(codegenNode, context)
}
root.codegenNode = codegenNode
} else {

View File

@ -12,11 +12,13 @@ import {
ParentNode,
JSChildNode,
CallExpression,
createArrayExpression
createArrayExpression,
getVNodeBlockHelper,
getVNodeHelper
} from '../ast'
import { TransformContext } from '../transform'
import { PatchFlags, isString, isSymbol, isArray } from '@vue/shared'
import { getVNodeBlockHelper, getVNodeHelper, isSlotOutlet } from '../utils'
import { isSlotOutlet } from '../utils'
import {
OPEN_BLOCK,
GUARD_REACTIVE_PROPS,

View File

@ -26,7 +26,9 @@ import {
ForIteratorExpression,
ConstantTypes,
createBlockStatement,
createCompoundExpression
createCompoundExpression,
getVNodeBlockHelper,
getVNodeHelper
} from '../ast'
import { createCompilerError, ErrorCodes } from '../errors'
import {
@ -35,8 +37,6 @@ import {
isTemplateNode,
isSlotOutlet,
injectProp,
getVNodeBlockHelper,
getVNodeHelper,
findDir
} from '../utils'
import {

View File

@ -23,7 +23,8 @@ import {
locStub,
CacheExpression,
ConstantTypes,
MemoExpression
MemoExpression,
convertToBlock
} from '../ast'
import { createCompilerError, ErrorCodes } from '../errors'
import { processExpression } from './transformExpression'
@ -34,10 +35,9 @@ import {
findDir,
findProp,
isBuiltInType,
makeBlock
getMemoedVNodeCall
} from '../utils'
import { PatchFlags, PatchFlagNames } from '@vue/shared'
import { getMemoedVNodeCall } from '..'
export const transformIf = createStructuralDirectiveTransform(
/^(if|else|else-if)$/,
@ -301,7 +301,7 @@ function createChildrenCodegenNode(
const vnodeCall = getMemoedVNodeCall(ret)
// Change createVNode to createBlock.
if (vnodeCall.type === NodeTypes.VNODE_CALL) {
makeBlock(vnodeCall, context)
convertToBlock(vnodeCall, context)
}
// inject branch key
injectProp(vnodeCall, keyProperty, context)

View File

@ -1,6 +1,7 @@
import { NodeTransform } from '../transform'
import { findDir, makeBlock } from '../utils'
import { findDir } from '../utils'
import {
convertToBlock,
createCallExpression,
createFunctionExpression,
ElementTypes,
@ -26,7 +27,7 @@ export const transformMemo: NodeTransform = (node, context) => {
if (codegenNode && codegenNode.type === NodeTypes.VNODE_CALL) {
// non-component sub tree should be turned into a block
if (node.tagType !== ElementTypes.COMPONENT) {
makeBlock(codegenNode, context)
convertToBlock(codegenNode, context)
}
node.codegenNode = createCallExpression(context.helper(WITH_MEMO), [
dir.exp!,

View File

@ -35,12 +35,7 @@ import {
TO_HANDLERS,
NORMALIZE_PROPS,
GUARD_REACTIVE_PROPS,
CREATE_BLOCK,
CREATE_ELEMENT_BLOCK,
CREATE_VNODE,
CREATE_ELEMENT_VNODE,
WITH_MEMO,
OPEN_BLOCK
WITH_MEMO
} from './runtimeHelpers'
import { isString, isObject, hyphenate, extend, NOOP } from '@vue/shared'
import { PropsExpression } from './transforms/transformElement'
@ -331,14 +326,6 @@ export function isSlotOutlet(
return node.type === NodeTypes.ELEMENT && node.tagType === ElementTypes.SLOT
}
export function getVNodeHelper(ssr: boolean, isComponent: boolean) {
return ssr || isComponent ? CREATE_VNODE : CREATE_ELEMENT_VNODE
}
export function getVNodeBlockHelper(ssr: boolean, isComponent: boolean) {
return ssr || isComponent ? CREATE_BLOCK : CREATE_ELEMENT_BLOCK
}
const propsHelperSet = new Set([NORMALIZE_PROPS, GUARD_REACTIVE_PROPS])
function getUnnormalizedProps(
@ -532,15 +519,3 @@ export function getMemoedVNodeCall(node: BlockCodegenNode | MemoExpression) {
return node
}
}
export function makeBlock(
node: VNodeCall,
{ helper, removeHelper, inSSR }: TransformContext
) {
if (!node.isBlock) {
node.isBlock = true
removeHelper(getVNodeHelper(inSSR, node.isComponent))
helper(OPEN_BLOCK)
helper(getVNodeBlockHelper(inSSR, node.isComponent))
}
}

View File

@ -0,0 +1,183 @@
import { makeMap } from './makeMap'
export const EMPTY_OBJ: { readonly [key: string]: any } = __DEV__
? Object.freeze({})
: {}
export const EMPTY_ARR = __DEV__ ? Object.freeze([]) : []
export const NOOP = () => {}
/**
* Always return false.
*/
export const NO = () => false
const onRE = /^on[^a-z]/
export const isOn = (key: string) => onRE.test(key)
export const isModelListener = (key: string) => key.startsWith('onUpdate:')
export const extend = Object.assign
export const remove = <T>(arr: T[], el: T) => {
const i = arr.indexOf(el)
if (i > -1) {
arr.splice(i, 1)
}
}
const hasOwnProperty = Object.prototype.hasOwnProperty
export const hasOwn = (
val: object,
key: string | symbol
): key is keyof typeof val => hasOwnProperty.call(val, key)
export const isArray = Array.isArray
export const isMap = (val: unknown): val is Map<any, any> =>
toTypeString(val) === '[object Map]'
export const isSet = (val: unknown): val is Set<any> =>
toTypeString(val) === '[object Set]'
export const isDate = (val: unknown): val is Date =>
toTypeString(val) === '[object Date]'
export const isRegExp = (val: unknown): val is RegExp =>
toTypeString(val) === '[object RegExp]'
export const isFunction = (val: unknown): val is Function =>
typeof val === 'function'
export const isString = (val: unknown): val is string => typeof val === 'string'
export const isSymbol = (val: unknown): val is symbol => typeof val === 'symbol'
export const isObject = (val: unknown): val is Record<any, any> =>
val !== null && typeof val === 'object'
export const isPromise = <T = any>(val: unknown): val is Promise<T> => {
return isObject(val) && isFunction(val.then) && isFunction(val.catch)
}
export const objectToString = Object.prototype.toString
export const toTypeString = (value: unknown): string =>
objectToString.call(value)
export const toRawType = (value: unknown): string => {
// extract "RawType" from strings like "[object RawType]"
return toTypeString(value).slice(8, -1)
}
export const isPlainObject = (val: unknown): val is object =>
toTypeString(val) === '[object Object]'
export const isIntegerKey = (key: unknown) =>
isString(key) &&
key !== 'NaN' &&
key[0] !== '-' &&
'' + parseInt(key, 10) === key
export const isReservedProp = /*#__PURE__*/ makeMap(
// the leading comma is intentional so empty string "" is also included
',key,ref,ref_for,ref_key,' +
'onVnodeBeforeMount,onVnodeMounted,' +
'onVnodeBeforeUpdate,onVnodeUpdated,' +
'onVnodeBeforeUnmount,onVnodeUnmounted'
)
export const isBuiltInDirective = /*#__PURE__*/ makeMap(
'bind,cloak,else-if,else,for,html,if,model,on,once,pre,show,slot,text,memo'
)
const cacheStringFunction = <T extends (str: string) => string>(fn: T): T => {
const cache: Record<string, string> = Object.create(null)
return ((str: string) => {
const hit = cache[str]
return hit || (cache[str] = fn(str))
}) as T
}
const camelizeRE = /-(\w)/g
/**
* @private
*/
export const camelize = cacheStringFunction((str: string): string => {
return str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : ''))
})
const hyphenateRE = /\B([A-Z])/g
/**
* @private
*/
export const hyphenate = cacheStringFunction((str: string) =>
str.replace(hyphenateRE, '-$1').toLowerCase()
)
/**
* @private
*/
export const capitalize = cacheStringFunction(
(str: string) => str.charAt(0).toUpperCase() + str.slice(1)
)
/**
* @private
*/
export const toHandlerKey = cacheStringFunction((str: string) =>
str ? `on${capitalize(str)}` : ``
)
// compare whether a value has changed, accounting for NaN.
export const hasChanged = (value: any, oldValue: any): boolean =>
!Object.is(value, oldValue)
export const invokeArrayFns = (fns: Function[], arg?: any) => {
for (let i = 0; i < fns.length; i++) {
fns[i](arg)
}
}
export const def = (obj: object, key: string | symbol, value: any) => {
Object.defineProperty(obj, key, {
configurable: true,
enumerable: false,
value
})
}
/**
* "123-foo" will be parsed to 123
* This is used for the .number modifier in v-model
*/
export const looseToNumber = (val: any): any => {
const n = parseFloat(val)
return isNaN(n) ? val : n
}
/**
* Only conerces number-like strings
* "123-foo" will be returned as-is
*/
export const toNumber = (val: any): any => {
const n = isString(val) ? Number(val) : NaN
return isNaN(n) ? val : n
}
let _globalThis: any
export const getGlobalThis = (): any => {
return (
_globalThis ||
(_globalThis =
typeof globalThis !== 'undefined'
? globalThis
: typeof self !== 'undefined'
? self
: typeof window !== 'undefined'
? window
: typeof global !== 'undefined'
? global
: {})
)
}
const identRE = /^[_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*$/
export function genPropsAccessExp(name: string) {
return identRE.test(name)
? `__props.${name}`
: `__props[${JSON.stringify(name)}]`
}

View File

@ -1,6 +1,5 @@
import { makeMap } from './makeMap'
export { makeMap }
export { makeMap } from './makeMap'
export * from './general'
export * from './patchFlags'
export * from './shapeFlags'
export * from './slotFlags'
@ -13,185 +12,3 @@ export * from './escapeHtml'
export * from './looseEqual'
export * from './toDisplayString'
export * from './typeUtils'
export const EMPTY_OBJ: { readonly [key: string]: any } = __DEV__
? Object.freeze({})
: {}
export const EMPTY_ARR = __DEV__ ? Object.freeze([]) : []
export const NOOP = () => {}
/**
* Always return false.
*/
export const NO = () => false
const onRE = /^on[^a-z]/
export const isOn = (key: string) => onRE.test(key)
export const isModelListener = (key: string) => key.startsWith('onUpdate:')
export const extend = Object.assign
export const remove = <T>(arr: T[], el: T) => {
const i = arr.indexOf(el)
if (i > -1) {
arr.splice(i, 1)
}
}
const hasOwnProperty = Object.prototype.hasOwnProperty
export const hasOwn = (
val: object,
key: string | symbol
): key is keyof typeof val => hasOwnProperty.call(val, key)
export const isArray = Array.isArray
export const isMap = (val: unknown): val is Map<any, any> =>
toTypeString(val) === '[object Map]'
export const isSet = (val: unknown): val is Set<any> =>
toTypeString(val) === '[object Set]'
export const isDate = (val: unknown): val is Date =>
toTypeString(val) === '[object Date]'
export const isRegExp = (val: unknown): val is RegExp =>
toTypeString(val) === '[object RegExp]'
export const isFunction = (val: unknown): val is Function =>
typeof val === 'function'
export const isString = (val: unknown): val is string => typeof val === 'string'
export const isSymbol = (val: unknown): val is symbol => typeof val === 'symbol'
export const isObject = (val: unknown): val is Record<any, any> =>
val !== null && typeof val === 'object'
export const isPromise = <T = any>(val: unknown): val is Promise<T> => {
return isObject(val) && isFunction(val.then) && isFunction(val.catch)
}
export const objectToString = Object.prototype.toString
export const toTypeString = (value: unknown): string =>
objectToString.call(value)
export const toRawType = (value: unknown): string => {
// extract "RawType" from strings like "[object RawType]"
return toTypeString(value).slice(8, -1)
}
export const isPlainObject = (val: unknown): val is object =>
toTypeString(val) === '[object Object]'
export const isIntegerKey = (key: unknown) =>
isString(key) &&
key !== 'NaN' &&
key[0] !== '-' &&
'' + parseInt(key, 10) === key
export const isReservedProp = /*#__PURE__*/ makeMap(
// the leading comma is intentional so empty string "" is also included
',key,ref,ref_for,ref_key,' +
'onVnodeBeforeMount,onVnodeMounted,' +
'onVnodeBeforeUpdate,onVnodeUpdated,' +
'onVnodeBeforeUnmount,onVnodeUnmounted'
)
export const isBuiltInDirective = /*#__PURE__*/ makeMap(
'bind,cloak,else-if,else,for,html,if,model,on,once,pre,show,slot,text,memo'
)
const cacheStringFunction = <T extends (str: string) => string>(fn: T): T => {
const cache: Record<string, string> = Object.create(null)
return ((str: string) => {
const hit = cache[str]
return hit || (cache[str] = fn(str))
}) as T
}
const camelizeRE = /-(\w)/g
/**
* @private
*/
export const camelize = cacheStringFunction((str: string): string => {
return str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : ''))
})
const hyphenateRE = /\B([A-Z])/g
/**
* @private
*/
export const hyphenate = cacheStringFunction((str: string) =>
str.replace(hyphenateRE, '-$1').toLowerCase()
)
/**
* @private
*/
export const capitalize = cacheStringFunction(
(str: string) => str.charAt(0).toUpperCase() + str.slice(1)
)
/**
* @private
*/
export const toHandlerKey = cacheStringFunction((str: string) =>
str ? `on${capitalize(str)}` : ``
)
// compare whether a value has changed, accounting for NaN.
export const hasChanged = (value: any, oldValue: any): boolean =>
!Object.is(value, oldValue)
export const invokeArrayFns = (fns: Function[], arg?: any) => {
for (let i = 0; i < fns.length; i++) {
fns[i](arg)
}
}
export const def = (obj: object, key: string | symbol, value: any) => {
Object.defineProperty(obj, key, {
configurable: true,
enumerable: false,
value
})
}
/**
* "123-foo" will be parsed to 123
* This is used for the .number modifier in v-model
*/
export const looseToNumber = (val: any): any => {
const n = parseFloat(val)
return isNaN(n) ? val : n
}
/**
* Only conerces number-like strings
* "123-foo" will be returned as-is
*/
export const toNumber = (val: any): any => {
const n = isString(val) ? Number(val) : NaN
return isNaN(n) ? val : n
}
let _globalThis: any
export const getGlobalThis = (): any => {
return (
_globalThis ||
(_globalThis =
typeof globalThis !== 'undefined'
? globalThis
: typeof self !== 'undefined'
? self
: typeof window !== 'undefined'
? window
: typeof global !== 'undefined'
? global
: {})
)
}
const identRE = /^[_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*$/
export function genPropsAccessExp(name: string) {
return identRE.test(name)
? `__props.${name}`
: `__props[${JSON.stringify(name)}]`
}

View File

@ -1,4 +1,4 @@
import { isArray, isDate, isObject, isSymbol } from './'
import { isArray, isDate, isObject, isSymbol } from './general'
function looseCompareArrays(a: any[], b: any[]) {
if (a.length !== b.length) return false

View File

@ -1,4 +1,4 @@
import { isArray, isString, isObject, hyphenate } from './'
import { isArray, isString, isObject, hyphenate } from './general'
export type NormalizedStyle = Record<string, string | number>

View File

@ -7,7 +7,7 @@ import {
isSet,
objectToString,
isString
} from './index'
} from './general'
/**
* For converting {{ interpolation }} values to displayed strings.